Commit 169ecf6f authored by Thomas Gambier's avatar Thomas Gambier 🚴🏼 Committed by Lu Xu

erp5.util: add a new test type to do real requests to the slapos master

This was previously known as "SlapOS Agent".
parent a57e0e2f
...@@ -489,6 +489,12 @@ class TaskDistributor(RPCRetry): ...@@ -489,6 +489,12 @@ class TaskDistributor(RPCRetry):
""" """
return self._retryRPC('getSlaposHateoasUrl') return self._retryRPC('getSlaposHateoasUrl')
def getServiceList(self, test_suite_title):
"""
Returns the list of services needed to run the test
"""
return self._retryRPC('getServiceList', (test_suite_title,))
def createTestResult(self, revision, test_name_list, node_title, def createTestResult(self, revision, test_name_list, node_title,
allow_restart=False, test_title=None, project_title=None): allow_restart=False, test_title=None, project_title=None):
""" """
......
##############################################################################
#
# Copyright (c) 2011 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.
#
##############################################################################
import datetime
import os
import subprocess
import sys
import time
import glob
from . import SlapOSControler, SlapOSMasterCommunicator
import json
import time
import shutil
import logging
import string
import random
from six.moves.urllib.parse import urlparse
import base64
from six.moves import http_client as httplib
from .Utils import createFolder, deunicodeData, dealShebang
from slapos.grid.utils import md5digest
import requests
import slapos.slap
from .ProcessManager import SubprocessError, ProcessManager, CancellationError, format_command
from subprocess import CalledProcessError
from .Updater import Updater
from erp5.util import taskdistribution
from erp5.util.benchmark.thread import TestThread
# for dummy slapos answer
import signal
from . import logger
from six.moves import range
# max time to generate frontend instance: 1.5 hour
MAX_FRONTEND_TIME = 60*90
# max time to register instance to slapOSMaster: 5 minutes
MAX_CREATION_INSTANCE_TIME = 60*10
# max time for a test: 20 minutes
MAX_TEST_CASE_TIME = 60*20
# max time to prepare SlapOS for testsuite (software installation, instances requests, etc.)
MAX_PREPARE_TEST_SUITE = 3600*10*1.0 # 10 hours
# max time for a test line creation: 5 minutes
MAX_CREATION_TEST_LINE = 60*10
# max time for bootstrapping an instance site
MAX_BOOTSRAPPING_TIME = 60*30
# max time to get a connection
MAX_CONNECTION_TIME = 60*5
# time to check site bootstrap
CHECK_BOOSTRAPPING_TIME = 60*2
# runner names
PERFORMANCE_RUNNER_SCRIPT = "performance_tester_erp5"
REQUEST_URL_SCRIPT = "requestUrl"
SCALABILITY_TEST = "scalability_test"
TEST_SUITE_INIT = "__init__.py"
# access SR by password
TESTNODE_USER = "testnode"
HTACCESS = "/.htaccess"
HTPASSWD = "/.htpasswd"
PASSWORD_FILE = "/sr_pass"
PASSWORD_LENGTH = 10
HOSTFILE = "/hosts"
SR_DICT = "frontend_software_dict"
INSTANCE_DICT = "instances_dict"
class RealRequestRunner():
def __init__(self, testnode):
self.testnode = testnode
self.slapos_controler = SlapOSControler.SlapOSControler(
self.testnode.working_directory,
self.testnode.config)
# Create the slapos account configuration file and dir
key = self.testnode.taskdistribution.getSlaposAccountKey()
certificate = self.testnode.taskdistribution.getSlaposAccountCertificate()
# Get Slapos Master Url
self.slapos_url = ''
try:
self.slapos_url = self.testnode.taskdistribution.getSlaposUrl()
if not self.slapos_url:
self.slapos_url = self.testnode.config['server_url']
except Exception:
self.slapos_url = self.testnode.config['server_url']
# Get Slapos Master url used for api rest (using hateoas)
self.slapos_api_rest_url = self.testnode.taskdistribution.getSlaposHateoasUrl()
logger.info("SlapOS Master url is: %s", self.slapos_url)
logger.info("SlapOS Master hateoas url is: %s", self.slapos_api_rest_url)
self.key_path, self.cert_path, config_path = self.slapos_controler.createSlaposConfigurationFileAccount(
key, certificate, self.slapos_url, self.testnode.config)
self.slapos_communicator_list = []
# Dict containing all info about SlapOS instances requested for the test
# looks like
# {
# instance1_title: {
#
#
# }
# }
self.instances_parameters_dict = {}
def _generateInstanceTitle(self, service_title):
"""
Generate an instance title using various parameter
TODO : add some verification (to don't use unexisting variables)
"""
instance_title = "E2E.Amarisoft-"
instance_title += "("+service_title+")-"
instance_title += str(datetime.datetime.now().isoformat())+"-"
instance_title += "timestamp="+str(time.time())
return instance_title
def getDictionaryFromFile(self, dict_file):
dictionary = {}
if os.path.isfile(dict_file):
with open(dict_file, 'r') as file:
dictionary = json.loads(file.read())
return dictionary
def updateDictionaryFile(self, dict_file, dictionary):
with open(dict_file, 'w') as file:
file.write(json.dumps(dictionary))
def _prepareSlapOS(self, working_directory, slapos_instance,
create_partition=1, software_path_list=None, use_local_shared_part=False, **kw):
"""
Launch slapos to build software and partitions
"""
slapproxy_log = os.path.join(self.testnode.config['log_directory'],
'slapproxy.log')
logger.debug('Configured slapproxy log to %r', slapproxy_log)
reset_software = slapos_instance.retry_software_count > 10
if reset_software:
slapos_instance.retry_software_count = 0
logger.info('testnode, retry_software_count: %r',
slapos_instance.retry_software_count)
self.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(\
self.slapos_controler.instance_root, 'var', 'run', 'supervisord.pid')
method_list= ["runSoftwareRelease"]
if create_partition:
method_list.append("runComputerPartition")
for method_name in method_list:
slapos_method = getattr(self.slapos_controler, method_name)
logger.debug("Before status_dict = slapos_method(...)")
status_dict = slapos_method(self.testnode.config,
environment=self.testnode.config['environment'],
**kw)
logger.info(status_dict)
logger.debug("After status_dict = slapos_method(...)")
if status_dict['status_code'] != 0:
slapos_instance.retry = True
slapos_instance.retry_software_count += 1
raise SubprocessError(status_dict)
else:
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
"""
return {'status_code' : 0}
def _initializeSlapOSConnection(self):
"""
Initialize communication with slapos
"""
slap = slapos.slap.slap()
retry = 0
while True:
# wait until _hateoas_navigator is loaded.
if retry > 100:
break
slap.initializeConnection(self.slapos_url,
self.key_path,
self.cert_path,
timeout=120,
slapgrid_rest_uri=self.slapos_api_rest_url)
if getattr(slap, '_hateoas_navigator', None) is None:
retry += 1
logger.info("Fail to load _hateoas_navigator waiting a bit and retry.")
time.sleep(30)
else:
break
if getattr(slap, '_hateoas_navigator', None) is None:
raise ValueError("Fail to load _hateoas_navigator")
supply = slap.registerSupply()
order = slap.registerOpenOrder()
return slap, supply, order
def prepareSlapOSForTestSuite(self, node_test_suite):
"""
Install testsuite softwares
"""
logger.debug('Preparing SlapOS for Test Suite...')
logger.info(node_test_suite)
result = self._prepareSlapOS(node_test_suite.working_directory,
node_test_suite,
software_path_list=[node_test_suite.custom_profile_path],
cluster_configuration={'_': json.dumps(node_test_suite.cluster_configuration)},
use_local_shared_part=True)
if result['status_code'] != 0:
return result
slappart_directory = self.testnode.config['srv_directory'].rsplit("srv", 1)[0]
instance_dict_file = slappart_directory + "var/" + INSTANCE_DICT
test_suite = node_test_suite.test_suite_title
# Initialize communication with slapos
slap, supply, order = self._initializeSlapOSConnection()
# Destroy previous instances
instance_dict = self.getDictionaryFromFile(instance_dict_file)
if test_suite in instance_dict:
instance_list = instance_dict[test_suite]
for instance_title,instance_is_shared in list(instance_list):
logger.info("Destroying previous instance %s", instance_title)
order.request(
software_release="dummy_SR_just_to_destroy", # XXX empty string is not supported...
partition_reference=instance_title,
shared=instance_is_shared,
state="destroyed")
del instance_list[0]
instance_dict[test_suite] = instance_list
self.updateDictionaryFile(instance_dict_file, instance_dict)
# Get from ERP5 Master the configuration of the cluster for the test
service_list = deunicodeData(
json.loads(self.testnode.taskdistribution.getServiceList(
node_test_suite.test_suite_title)))
logger.info('List of service is %s', service_list)
instance_list = [] # instance_list should already be an empty list (but it may not have been initialised if testsuite seen for the first time)
for service in service_list.values():
logger.info(service)
instance_title = self._generateInstanceTitle(service['title'])
software_release = service['url']
logger.info("Will request instance %s of %s (software_type %s)", instance_title, software_release, service['software_type'])
s = SlapOSMasterCommunicator.SlapOSTester(
instance_title,
slap,
order,
supply,
software_release)
self.slapos_communicator_list.append(s)
partition_parameter_kw = json.loads(service['partition_parameter_kw'])
if len(partition_parameter_kw) == 1 and '_' in partition_parameter_kw:
partition_parameter_kw['_'] = json.dumps(partition_parameter_kw ['_'])
request_kw = {
'partition_parameter_kw' : partition_parameter_kw,
'filter_kw' : json.loads(service['filter_kw'])
}
s.requestInstanceStart(
request_kw=request_kw,
shared=service['shared'],
software_type=service['software_type']
)
instance_list.append((instance_title,service['shared']))
instance_dict[test_suite] = instance_list
self.updateDictionaryFile(instance_dict_file, instance_dict)
logger.debug("Instances requested.")
return {'status_code' : 0}
def runTestSuite(self, node_test_suite, portal_url):
# Wait all instances are started
for s in self.slapos_communicator_list:
try:
s.waitInstanceStarted()
self.instances_parameters_dict[s.name] = s.getInstanceParameterDict()
except Exception as e:
error_message = "Error starting instance " + s.name + ": " + str(e)
return {'status_code' : 1, 'error_message': error_message }
instances_parameters_file = os.path.join(self.testnode.config['srv_directory'], "instances.json")
with open(instances_parameters_file, 'w') as file:
file.write(json.dumps(self.instances_parameters_dict))
logger.debug("ALL INSTANCES CORRECTLY STARTED")
config = self.testnode.config
run_test_suite_path_list = glob.glob(
self.slapos_controler.instance_root + "/*/bin/runTestSuite")
try:
run_test_suite_path = min(run_test_suite_path_list)
except ValueError:
raise ValueError('No runTestSuite provided in installed partitions.')
# Deal with Shebang size limitation
invocation_list = dealShebang(run_test_suite_path)
invocation_list += (run_test_suite_path,
'--master_url', portal_url,
'--revision', node_test_suite.revision,
'--test_node_title', config['test_node_title'],
'--test_suite', node_test_suite.test_suite,
'--test_suite_title', node_test_suite.test_suite_title)
soft = config['slapos_directory'] + '/soft/'
software_list = [soft + md5digest(x) for x in config['software_list']]
PATH = os.getenv('PATH', '')
PATH = ':'.join(x + '/bin' for x in software_list) + (PATH and ':' + PATH)
SLAPOS_TEST_SHARED_PART_LIST = os.pathsep.join(
self.slapos_controler.shared_part_list)
SLAPOS_TEST_LOG_DIRECTORY = node_test_suite.log_folder_path
supported_parameter_set = set(self.testnode.process_manager
.getSupportedParameterList(run_test_suite_path))
def path(name, compat): # BBB
path, = filter(os.path.exists, (base + relative
for relative in ('/bin/' + name, '/parts/' + compat)
for base in software_list))
return path
for option, value in (
('--firefox_bin', lambda: path('firefox', 'firefox/firefox-slapos')),
('--frontend_url', lambda: config['frontend_url']),
('--node_quantity', lambda: config['node_quantity']),
('--xvfb_bin', lambda: path('xvfb', 'xserver/bin/Xvfb')),
('--project_title', lambda: node_test_suite.project_title),
('--shared_part_list', lambda: SLAPOS_TEST_SHARED_PART_LIST),
('--log_directory', lambda: SLAPOS_TEST_LOG_DIRECTORY),
):
if option in supported_parameter_set:
invocation_list += option, value()
# TODO : include testnode correction ( b111682f14890bf )
if hasattr(node_test_suite,'additional_bt5_repository_id'):
additional_bt5_path = os.path.join(
node_test_suite.working_directory,
node_test_suite.additional_bt5_repository_id)
invocation_list.extend(["--bt5_path", additional_bt5_path])
# From this point, test runner becomes responsible for updating test
# result. We only do cleanup if the test runner itself is not able
# to run.
createFolder(node_test_suite.test_suite_directory, clean=True)
# Log the actual command with root logger
root_logger = logging.getLogger()
root_logger.info(
"Running test suite with: %s",
format_command(*invocation_list, PATH=PATH))
def hide_distributor_url(s):
# type: (bytes) -> bytes
return s.replace(portal_url.encode('utf-8'), b'$DISTRIBUTOR_URL')
self.testnode.process_manager.spawn(*invocation_list, PATH=PATH,
SLAPOS_TEST_SHARED_PART_LIST=SLAPOS_TEST_SHARED_PART_LIST,
SLAPOS_TEST_LOG_DIRECTORY=SLAPOS_TEST_LOG_DIRECTORY,
SLAPOS_INSTANCES_PARAMETERS_FILE=instances_parameters_file,
cwd=node_test_suite.test_suite_directory,
log_prefix='runTestSuite',
output_replacers=(hide_distributor_url,),
get_output=False)
return {'status_code' : 0}
def getRelativePathUsage(self):
"""
Used by the method testnode.constructProfile() to know
if the software.cfg have to use relative path or not.
"""
return False
...@@ -386,10 +386,11 @@ Require valid-user ...@@ -386,10 +386,11 @@ Require valid-user
self.error_message = test_configuration['error_message'] self.error_message = test_configuration['error_message']
self.randomized_path = test_configuration['randomized_path'] self.randomized_path = test_configuration['randomized_path']
if not self.launchable: if not self.launchable:
logger.info("Test suite %s is not actually launchable" error_message = ("Test suite %s is not actually launchable"
" with the current cluster configuration.", node_test_suite.test_suite_title) " with the current cluster configuration.\n"
logger.info("ERP5 Master indicates : %s", self.error_message) "ERP5 Master indicates : %s" % (node_test_suite.test_suite_title, self.error_message))
return {'status_code' : 1} logger.info(error_message)
return {'status_code' : 1, 'error_message' : error_message}
configuration_list = test_configuration['configuration_list'] configuration_list = test_configuration['configuration_list']
configuration = configuration_list[0] configuration = configuration_list[0]
...@@ -451,8 +452,9 @@ Require valid-user ...@@ -451,8 +452,9 @@ Require valid-user
self._comeBackFromDummySlapOS() self._comeBackFromDummySlapOS()
if self.remainSoftwareToInstall() : if self.remainSoftwareToInstall() :
# All softwares are not installed, however maxtime is elapsed, that's a failure. # All softwares are not installed, however maxtime is elapsed, that's a failure.
logger.error("All softwares are not installed.") error_message = "All softwares are not installed."
return {'status_code' : 1} logger.error(error_message)
return {'status_code' : 1, 'error_message' : error_message}
logger.debug("All software installed.") logger.debug("All software installed.")
# even if we re-use existing setup we need proper configuration applied # even if we re-use existing setup we need proper configuration applied
...@@ -469,11 +471,12 @@ Require valid-user ...@@ -469,11 +471,12 @@ Require valid-user
purge_previous_instance = not self.use_existing_setup) purge_previous_instance = not self.use_existing_setup)
logger.debug("Scalability instance requested.") logger.debug("Scalability instance requested.")
except Exception as e: except Exception as e:
logger.error("Error creating instance: " + str(e)) error_message = "Error creating instance: " + str(e)
return {'status_code' : 1} logger.error(error_message)
return {'status_code' : 1, 'error_message' : error_message}
return {'status_code' : 0} return {'status_code' : 0}
return {'status_code' : 1} return {'status_code' : 1, 'error_message' : "Software installation too long or error(s) are present during SR install."}
def makeSuite(self, test_suite, location_list, **kwargs): def makeSuite(self, test_suite, location_list, **kwargs):
import imp import imp
......
from __future__ import print_function from __future__ import print_function
import datetime import datetime
import feedparser
import json import json
import traceback import traceback
import time import time
...@@ -71,6 +72,8 @@ class SlapOSMasterCommunicator(object): ...@@ -71,6 +72,8 @@ class SlapOSMasterCommunicator(object):
self.slap_order = slap_order self.slap_order = slap_order
self.slap_supply = slap_supply self.slap_supply = slap_supply
self.hateoas_navigator = self.slap._hateoas_navigator self.hateoas_navigator = self.slap._hateoas_navigator
self.message_history = []
self.computer_guid = ""
if url is not None and \ if url is not None and \
url.startswith(SOFTWARE_PRODUCT_NAMESPACE): url.startswith(SOFTWARE_PRODUCT_NAMESPACE):
...@@ -79,7 +82,7 @@ class SlapOSMasterCommunicator(object): ...@@ -79,7 +82,7 @@ class SlapOSMasterCommunicator(object):
try: try:
url = product.__getattr__(url[len(SOFTWARE_PRODUCT_NAMESPACE):]) url = product.__getattr__(url[len(SOFTWARE_PRODUCT_NAMESPACE):])
except AttributeError as e: except AttributeError as e:
logger.warning('Error on get software release: %s ', e.message) logger.warning("Error on get software release: {}".format(e))
self.url = url self.url = url
...@@ -115,6 +118,7 @@ class SlapOSMasterCommunicator(object): ...@@ -115,6 +118,7 @@ class SlapOSMasterCommunicator(object):
partition_reference=self.name, partition_reference=self.name,
shared=shared, shared=shared,
state=state, state=state,
software_type=software_type,
**self.request_kw) **self.request_kw)
@retryOnNetworkFailure @retryOnNetworkFailure
...@@ -183,16 +187,18 @@ class SlapOSMasterCommunicator(object): ...@@ -183,16 +187,18 @@ class SlapOSMasterCommunicator(object):
message_list = [] message_list = []
try: try:
for instance in self.getInstanceUrlList(): for instance in self.getInstanceUrlList():
# we need to explicitly encode as utf-8 the unicode string we get logger.info('in _getInstanceState, viewing instance')
instance["text_content"] = instance["text_content"].encode('utf8') logger.info(instance)
news = instance['SoftwareInstance_getNewsDict'] news = instance['SoftwareInstance_getNewsDict']
state = INSTANCE_STATE_UNKNOWN state = INSTANCE_STATE_UNKNOWN
monitor_information_dict = {} monitor_information_dict = {}
is_slave = instance['portal_type'] == "Slave Instance" is_slave = instance['portal_type'] == "Slave Instance"
if is_slave: if is_slave:
if len(instance['getConnectionXmlAsDict']) > 0: # XXX for now consider a slave as always ready because in ORS software
state = INSTANCE_STATE_STARTED # there is no information published in the slave
#if len(instance['getConnectionXmlAsDict']) > 0:
state = INSTANCE_STATE_STARTED
else: else:
# not slave # not slave
instance_state = news instance_state = news
...@@ -300,7 +306,6 @@ class SlapOSTester(SlapOSMasterCommunicator): ...@@ -300,7 +306,6 @@ class SlapOSTester(SlapOSMasterCommunicator):
self.request_kw = json.loads(request_kw) self.request_kw = json.loads(request_kw)
else: else:
self.request_kw = request_kw self.request_kw = request_kw
self.message_history = []
def getInfo(self): def getInfo(self):
info = "" info = ""
...@@ -323,12 +328,13 @@ class SlapOSTester(SlapOSMasterCommunicator): ...@@ -323,12 +328,13 @@ class SlapOSTester(SlapOSMasterCommunicator):
def requestInstanceStop(self, instance_title=None, request_kw=None, shared=False, software_type="RootSoftwareInstance"): def requestInstanceStop(self, instance_title=None, request_kw=None, shared=False, software_type="RootSoftwareInstance"):
self.instance = self._request(INSTANCE_STATE_STOPPED, instance_title, request_kw, shared, software_type) self.instance = self._request(INSTANCE_STATE_STOPPED, instance_title, request_kw, shared, software_type)
def requestInstanceDestroy(self, instance_title=None, request_kw=None, shared=False): def requestInstanceDestroy(self):
# TODO remove this function
self.destroyInstance()
def waitInstanceStarted(self, instance_title=None):
if not instance_title: if not instance_title:
instance_title = self.name instance_title = self.name
self.destroyInstance(instance_title)
def waitInstanceStarted(self, instance_title):
error_message = self._waitInstance(instance_title, INSTANCE_STATE_STARTED)["error_message"] error_message = self._waitInstance(instance_title, INSTANCE_STATE_STARTED)["error_message"]
if error_message is not None: if error_message is not None:
logger.error(error_message) logger.error(error_message)
...@@ -349,6 +355,13 @@ class SlapOSTester(SlapOSMasterCommunicator): ...@@ -349,6 +355,13 @@ class SlapOSTester(SlapOSMasterCommunicator):
logger.error(error_message) logger.error(error_message)
raise ValueError(error_message) raise ValueError(error_message)
def getInstanceParameterDict(self):
for instance in self.getInstanceUrlList():
if instance["title"] == self.name:
return instance["getConnectionXmlAsDict"]
return {}
def getMasterFrontendDict(self): def getMasterFrontendDict(self):
def getInstanceGuid(): def getInstanceGuid():
try: try:
...@@ -365,7 +378,7 @@ class SlapOSTester(SlapOSMasterCommunicator): ...@@ -365,7 +378,7 @@ class SlapOSTester(SlapOSMasterCommunicator):
pass pass
start_time = time.time() start_time = time.time()
while not getInstanceGuid() and time.time()-start_time < 60*5: while not getInstanceGuid() and time.time()-start_time < 60*5:
sleep(60) time.sleep(60)
return {'instance_guid' : getInstanceGuid(), 'frontend_master_ipv6' : frontend_master_ipv6} return {'instance_guid' : getInstanceGuid(), 'frontend_master_ipv6' : frontend_master_ipv6}
# XXX TODO # XXX TODO
...@@ -406,16 +419,15 @@ class SlapOSTester(SlapOSMasterCommunicator): ...@@ -406,16 +419,15 @@ class SlapOSTester(SlapOSMasterCommunicator):
'frontend-url-list' : frontend_url_list, \ 'frontend-url-list' : frontend_url_list, \
'balancer-user-v6': balancer_user_v6 } 'balancer-user-v6': balancer_user_v6 }
def destroyInstance(self, instance_title): def destroyInstance(self):
self.name = instance_title
instance_url_list = self.getInstanceUrlList() instance_url_list = self.getInstanceUrlList()
if instance_url_list: if instance_url_list:
for instance in instance_url_list: for instance in instance_url_list:
if instance["title"] != instance_title: if instance["title"] != self.name:
self._request(INSTANCE_STATE_DESTROYED, instance["title"]) self._request(INSTANCE_STATE_DESTROYED, instance["title"])
else: else:
root_instance = instance root_instance = instance
logger.info("Going to destroy root partition: " + str(instance_title)) logger.info("Going to destroy root partition: " + str(self.name))
self._request(INSTANCE_STATE_DESTROYED, root_instance["title"]) self._request(INSTANCE_STATE_DESTROYED, root_instance["title"])
else: else:
logger.info("Instance not found") logger.info("Instance not found")
...@@ -537,7 +549,7 @@ class SoftwareReleaseTester(SlapOSTester): ...@@ -537,7 +549,7 @@ class SoftwareReleaseTester(SlapOSTester):
deadline = self.deadline deadline = self.deadline
if deadline < now and deadline is not None: if deadline < now and deadline is not None:
raise TestTimeout(self.state) raise Exception("Test timeout (current state is {}).".format(self.state))
_, _, next_state, software_state, instance_state = self.transition_dict[ _, _, next_state, software_state, instance_state = self.transition_dict[
self.state] self.state]
......
...@@ -32,14 +32,10 @@ import logging ...@@ -32,14 +32,10 @@ import logging
from . import logger from . import logger
from .ProcessManager import SubprocessError, format_command from .ProcessManager import SubprocessError, format_command
from .SlapOSControler import SlapOSControler from .SlapOSControler import SlapOSControler
from .Utils import createFolder from .Utils import createFolder, dealShebang
from slapos.grid.utils import md5digest from slapos.grid.utils import md5digest
def dealShebang(run_test_suite_path):
with open(run_test_suite_path) as f:
if f.read(2) == '#!':
return f.readline().split(None, 1)
return []
class UnitTestRunner(object): class UnitTestRunner(object):
...@@ -203,6 +199,7 @@ class UnitTestRunner(object): ...@@ -203,6 +199,7 @@ class UnitTestRunner(object):
log_prefix='runTestSuite', log_prefix='runTestSuite',
output_replacers=(hide_distributor_url,), output_replacers=(hide_distributor_url,),
get_output=False) get_output=False)
return {'status_code' : 0}
def getRelativePathUsage(self): def getRelativePathUsage(self):
""" """
......
...@@ -10,6 +10,12 @@ def createFolder(folder, clean=False): ...@@ -10,6 +10,12 @@ def createFolder(folder, clean=False):
rmtree(folder) rmtree(folder)
os.mkdir(folder) os.mkdir(folder)
def dealShebang(run_test_suite_path):
with open(run_test_suite_path) as f:
if f.read(2) == '#!':
return f.readline().split(None, 1)
return []
if six.PY3: if six.PY3:
def deunicodeData(data): def deunicodeData(data):
return data return data
......
...@@ -38,6 +38,7 @@ from subprocess import CalledProcessError ...@@ -38,6 +38,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 .RealRequestRunner import RealRequestRunner
from .UnitTestRunner import UnitTestRunner from .UnitTestRunner import UnitTestRunner
from .Utils import deunicodeData from .Utils import deunicodeData
from .. import taskdistribution from .. import taskdistribution
...@@ -49,6 +50,7 @@ PROFILE_PATH_KEY = 'profile_path' ...@@ -49,6 +50,7 @@ PROFILE_PATH_KEY = 'profile_path'
test_type_registry = { test_type_registry = {
'UnitTest': UnitTestRunner, 'UnitTest': UnitTestRunner,
'ScalabilityTest': ScalabilityTestRunner, 'ScalabilityTest': ScalabilityTestRunner,
'SlapOSAgentTest': RealRequestRunner
} }
class TestNode(object): class TestNode(object):
...@@ -106,13 +108,13 @@ class TestNode(object): ...@@ -106,13 +108,13 @@ class TestNode(object):
# Absolute path to relative path # Absolute path to relative path
software_config_path = os.path.join(repository_path, profile_path) software_config_path = os.path.join(repository_path, profile_path)
if use_relative_path : if use_relative_path:
from_path = os.path.join(self.working_directory, from_path = os.path.join(self.working_directory,
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)
# Construct sections # Construct sections
if not(buildout_section_id is None): if buildout_section_id is not None:
# Absolute path to relative # Absolute path to relative
if use_relative_path: if use_relative_path:
from_path = os.path.join(self.working_directory, from_path = os.path.join(self.working_directory,
...@@ -320,7 +322,7 @@ shared = true ...@@ -320,7 +322,7 @@ shared = true
testnode_software_successfully_built = True testnode_software_successfully_built = True
logger.info("Will now skip build of testnode software") logger.info("Will now skip build of testnode software")
# Clean-up test suites # Clean-up test suites
self.purgeOldTestSuite(test_suite_data) #self.purgeOldTestSuite(test_suite_data)
for test_suite in test_suite_data: for test_suite in test_suite_data:
node_test_suite = self.getNodeTestSuite( node_test_suite = self.getNodeTestSuite(
test_suite.pop("test_suite_reference")) test_suite.pop("test_suite_reference"))
...@@ -354,6 +356,8 @@ shared = true ...@@ -354,6 +356,8 @@ shared = true
generated_config = taskdistributor.generateConfiguration( generated_config = taskdistributor.generateConfiguration(
node_test_suite.test_suite_title) node_test_suite.test_suite_title)
json_data = json.loads(generated_config) json_data = json.loads(generated_config)
logger.info("DEBUG JSON")
logger.info(json_data)
cluster_configuration = deunicodeData(json_data['configuration_list'][0]) cluster_configuration = deunicodeData(json_data['configuration_list'][0])
node_test_suite.edit(cluster_configuration=cluster_configuration) node_test_suite.edit(cluster_configuration=cluster_configuration)
# Now prepare the installation of SlapOS and create instance # Now prepare the installation of SlapOS and create instance
...@@ -364,35 +368,26 @@ shared = true ...@@ -364,35 +368,26 @@ shared = true
# should be at the same revision, so it is safe to prune orphan # should be at the same revision, so it is safe to prune orphan
# objects now. # objects now.
git_gc_auto() git_gc_auto()
def report_error(error_message):
test_result.reportFailure(
stdout=error_message
)
logger.error(error_message)
raise ValueError(error_message)
if status_dict['status_code'] == 1:
report_error(status_dict.get('error_message') or "Error during prepareSlapOSForTestSuite")
# 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(20)
# XXX: Do not switch according to the test type. IOW, the
# following code must be moved to the test type class.
if my_test_type == 'UnitTest':
runner.runTestSuite(node_test_suite, portal_url)
elif my_test_type == 'ScalabilityTest':
error_message = None
# A problem is appeared during runTestSuite
if status_dict['status_code'] == 1:
error_message = "Software installation too long or error(s) are present during SR install."
else:
status_dict = runner.runTestSuite(node_test_suite, portal_url)
# A problem is appeared during runTestSuite
if status_dict['status_code'] == 1:
error_message = status_dict['error_message']
# If an error is appeared runner.runTestSuite(node_test_suite, portal_url)
if error_message: status_dict = runner.runTestSuite(node_test_suite, portal_url)
test_result.reportFailure( # A problem is appeared during runTestSuite
stdout=error_message if status_dict['status_code'] == 1:
) report_error(status_dict.get('error_message') or 'Error during runTestSuite')
logger.error(error_message)
raise ValueError(error_message)
else:
raise NotImplementedError
# break the loop to get latest priorities from master
break break
except (SubprocessError, CalledProcessError, ConnectionError) as e: except (SubprocessError, CalledProcessError, ConnectionError) as e:
logger.exception("") logger.exception("")
......
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