Commit c8d33446 authored by Roque's avatar Roque

All my changes since beginning. Includes tasks: 1 to 4 (done) and 5 (current).

parent 646852df
...@@ -24,9 +24,6 @@ ...@@ -24,9 +24,6 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
# #
############################################################################## ##############################################################################
############## ROQUE VERSION #################################################
import datetime import datetime
import os import os
import subprocess import subprocess
...@@ -49,6 +46,10 @@ from erp5.util import taskdistribution ...@@ -49,6 +46,10 @@ from erp5.util import taskdistribution
# for dummy slapos answer # for dummy slapos answer
import signal import signal
import slapos.slap
# max time to instance changing state: 2 hour # max time to instance changing state: 2 hour
MAX_INSTANCE_TIME = 60*60*2 MAX_INSTANCE_TIME = 60*60*2
# max time to register instance to slapOSMaster: 5 minutes # max time to register instance to slapOSMaster: 5 minutes
...@@ -68,7 +69,6 @@ class ScalabilityTestRunner(): ...@@ -68,7 +69,6 @@ class ScalabilityTestRunner():
# Create the slapos account configuration file and dir # Create the slapos account configuration file and dir
key = self.testnode.test_suite_portal.getSlaposAccountKey() key = self.testnode.test_suite_portal.getSlaposAccountKey()
certificate = self.testnode.test_suite_portal.getSlaposAccountCertificate() certificate = self.testnode.test_suite_portal.getSlaposAccountCertificate()
# Get Slapos Master Url # Get Slapos Master Url
self.slapos_url = '' self.slapos_url = ''
try: try:
...@@ -85,7 +85,7 @@ class ScalabilityTestRunner(): ...@@ -85,7 +85,7 @@ class ScalabilityTestRunner():
self.log("SlapOS Master hateoas url is: %s" %self.slapos_api_rest_url) self.log("SlapOS Master hateoas url is: %s" %self.slapos_api_rest_url)
self.key_path, self.cert_path, config_path = self.slapos_controler.createSlaposConfigurationFileAccount( self.key_path, self.cert_path, config_path = self.slapos_controler.createSlaposConfigurationFileAccount(
key, certificate, self.slapos_url, self.testnode.config) key, certificate, self.slapos_url, self.testnode.config, self.slapos_api_rest_url)
self.slapos_communicator = None self.slapos_communicator = None
# Dict containing used to store which SR is not yet correctly installed. # Dict containing used to store which SR is not yet correctly installed.
# looks like: {'comp_id1':'SR_urlA', 'comp_id2':'SR_urlA',..} # looks like: {'comp_id1':'SR_urlA', 'comp_id2':'SR_urlA',..}
...@@ -106,7 +106,9 @@ class ScalabilityTestRunner(): ...@@ -106,7 +106,9 @@ class ScalabilityTestRunner():
self.log("testnode, supply : %s %s", software_path, computer_guid) self.log("testnode, supply : %s %s", software_path, computer_guid)
if self.authorize_supply : if self.authorize_supply :
self.remaining_software_installation_dict[computer_guid] = software_path self.remaining_software_installation_dict[computer_guid] = software_path
self.slapos_controler.supply(software_path, computer_guid) #self.slapos_controler.supply(software_path, computer_guid)
#self.slapos_communicator._supply("available")
# Here make a request via slapos controler ? # Here make a request via slapos controler ?
return {'status_code' : 0} return {'status_code' : 0}
else: else:
...@@ -150,9 +152,21 @@ class ScalabilityTestRunner(): ...@@ -150,9 +152,21 @@ class ScalabilityTestRunner():
test_result, test_suite) test_result, test_suite)
self.log("testnode, request : %s", instance_title) self.log("testnode, request : %s", instance_title)
config = json.dumps(config) config = json.dumps(config)
self.slapos_controler.request(instance_title, software_path, #request_kw = {"_" : config}
"test", {"_" : config}, request = {"_" : config}
self.launcher_nodes_computer_guid[0]) request_kw = {"partition_parameter_kw": request }
#self.log("config from softConf, tResult & tSuite: " + str(config))
#self.log("request_kw: " + str(request_kw))
self.log("Computer : " + str(self.launcher_nodes_computer_guid[0]))
#self.slapos_controler.request(instance_title, software_path,
# "test", {"_" : config},
# self.launcher_nodes_computer_guid[0])
self.slapos_communicator.setName(instance_title)
self.slapos_communicator.setRequestParameters(request_kw)
# ROQUE: request commented because it's failing. Instance manually requested.
#self.slapos_communicator._request("started")
# ROQUE: harcoded state (that should happens in the ._request )
self.slapos_communicator.forceSetState('started')
self.authorize_request = False self.authorize_request = False
return {'status_code' : 0} return {'status_code' : 0}
else: else:
...@@ -224,9 +238,11 @@ late a SlapOS (positive) answer." %(str(os.getpid()),str(os.getpid()),)) ...@@ -224,9 +238,11 @@ late a SlapOS (positive) answer." %(str(os.getpid()),str(os.getpid()),))
""" """
Wait for 'max_time' an instance specific state Wait for 'max_time' an instance specific state
""" """
max_time = 20
self.log("Wait for instance state: %s" %state) self.log("Wait for instance state: %s" %state)
start_time = time.time() start_time = time.time()
while (not self.slapos_communicator.isHostingSubscriptionReady(instance_title, state) #while (not self.slapos_communicator.isHostingSubscriptionReady(instance_title, state)
while (not self.slapos_communicator._getInstanceState() == state
and (max_time > (time.time()-start_time))): and (max_time > (time.time()-start_time))):
self.log("Instance(s) not in %s state yet." % state) self.log("Instance(s) not in %s state yet." % state)
time.sleep(15) time.sleep(15)
...@@ -243,13 +259,17 @@ late a SlapOS (positive) answer." %(str(os.getpid()),str(os.getpid()),)) ...@@ -243,13 +259,17 @@ late a SlapOS (positive) answer." %(str(os.getpid()),str(os.getpid()),))
raise ValueError(error_message) raise ValueError(error_message)
self.log("Instance correctly '%s' after %s seconds." %(state, str(time.time()-start_time))) self.log("Instance correctly '%s' after %s seconds." %(state, str(time.time()-start_time)))
def _waitInstanceCreation(self, instance_title, max_time=MAX_CREATION_INSTANCE_TIME): def _waitInstanceCreation(self, instance_title, hateoas, max_time=MAX_CREATION_INSTANCE_TIME):
""" """
Wait for 'max_time' the instance creation Wait for 'max_time' the instance creation
""" """
self.log("Wait for instance creation") self.log("Waiting for instance creation...")
start_time = time.time() start_time = time.time()
while ( not self.slapos_communicator.isRegisteredHostingSubscription(instance_title) \ #while ( not self.slapos_communicator.isRegisteredHostingSubscription(instance_title) \
self.log("Instance title: " + str(instance_title))
#self.log("List of Hosting subscriptions : ")
#self.log(str(hateoas.getHostingSubscriptionDict()))
while (not instance_title in hateoas.getHostingSubscriptionDict() \
and (max_time > (time.time()-start_time)) ): and (max_time > (time.time()-start_time)) ):
time.sleep(5) time.sleep(5)
if (time.time()-start_time) > max_time: if (time.time()-start_time) > max_time:
...@@ -260,18 +280,52 @@ late a SlapOS (positive) answer." %(str(os.getpid()),str(os.getpid()),)) ...@@ -260,18 +280,52 @@ late a SlapOS (positive) answer." %(str(os.getpid()),str(os.getpid()),))
""" """
Install testsuite softwares Install testsuite softwares
""" """
self.log('prepareSlapOSForTestSuite') self.log('Preparing SlapOS for Test Suite...')
# Define how many time this method can take # Define how many time this method can take
max_time = 3600*10*1.0 # 10 hours max_time = 3600*10*1.0 # 10 hours
interval_time = 60 interval_time = 60
start_time = time.time() start_time = time.time()
# Create a communicator with slapos #self.log("CERT: " + str(self.cert_path))
self.log("creating SlapOs Master communicator...") #self.log("KEY: " + str(self.key_path))
self.slapos_communicator = SlapOSMasterCommunicator.SlapOSMasterCommunicator( #self.log("API URL: " + str(self.slapos_api_rest_url))
self.cert_path, #self.log("MASTER URL: " + str(self.slapos_url))
self.key_path,
self.log, ### NEW ! Creating Slapos master communicator ###
self.slapos_api_rest_url) slap = slapos.slap.slap()
retry = 0
while True:
if retry > 100:
break
# wait until _hateoas_navigator is loaded.
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
self.log("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")
hateoas = getattr(slap, '_hateoas_navigator', None)
#self.log("_hateoas_navigator object ")
#self.log(".getHostingSubscriptionDict()")
#self.log(hateoas.getHostingSubscriptionDict())
#self.log("_hateoasGetInformation()")
#self.log(hateoas._hateoasGetInformation("nxdcloud-onlinenet-scalabilitynode-001-ERP5SCALABILITY"))
#self.log("getHostingSubscriptionRootSoftwareInstanceInformation(node)")
#self.log(hateoas.getHostingSubscriptionRootSoftwareInstanceInformation("nxdcloud-onlinenet-scalabilitynode-001-ERP5SCALABILITY"))
#self.log(".getRelatedInstanceInformation(node)")
#self.log(hateoas.getRelatedInstanceInformation("nxdcloud-onlinenet-scalabilitynode-001-ERP5SCALABILITY"))
supply = slap.registerSupply()
order = slap.registerOpenOrder()
### //NEW ! ###
# Only master testnode must order software installation # Only master testnode must order software installation
if self.testnode.test_suite_portal.isMasterTestnode( if self.testnode.test_suite_portal.isMasterTestnode(
self.testnode.config['test_node_title']): self.testnode.config['test_node_title']):
...@@ -340,7 +394,9 @@ late a SlapOS (positive) answer." %(str(os.getpid()),str(os.getpid()),)) ...@@ -340,7 +394,9 @@ late a SlapOS (positive) answer." %(str(os.getpid()),str(os.getpid()),))
# Ask for SR installation # Ask for SR installation
for computer_guid in self.involved_nodes_computer_guid: for computer_guid in self.involved_nodes_computer_guid:
self._prepareSlapOS(self.reachable_profile, computer_guid) self.slapos_communicator = SlapOSMasterCommunicator.SoftwareReleaseTester("NAME", self.log, slap, supply, order, self.reachable_profile, computer_guid=computer_guid)
# ROQUE: "_prepareSlapOS" is commented because the instance request is not working (manually created for dev purposes)
#self._prepareSlapOS(self.reachable_profile, computer_guid)
# From the line below we would not supply any more softwares # From the line below we would not supply any more softwares
self.authorize_supply = False self.authorize_supply = False
# TODO : remove the line below wich simulate an answer from slapos master # TODO : remove the line below wich simulate an answer from slapos master
...@@ -362,14 +418,49 @@ late a SlapOS (positive) answer." %(str(os.getpid()),str(os.getpid()),)) ...@@ -362,14 +418,49 @@ late a SlapOS (positive) answer." %(str(os.getpid()),str(os.getpid()),))
# Launch instance # Launch instance
self.instance_title = self._generateInstanceTitle(node_test_suite.test_suite_title) self.instance_title = self._generateInstanceTitle(node_test_suite.test_suite_title)
try: try:
# ROQUE: the instance title is harcoded because the instance request is not working (this one was manually created)
self.instance_title = "nxdcloud-onlinenet-scalabilitynode-001-TESTINSTANCE"
self._createInstance(self.reachable_profile, configuration_list[0], self._createInstance(self.reachable_profile, configuration_list[0],
self.instance_title, node_test_suite.test_result, node_test_suite.test_suite) self.instance_title, node_test_suite.test_result, node_test_suite.test_suite)
self.log("Scalability instance requested.") self.log("Scalability instance requested.")
except: except:
self.log("Unable to launch instance") self.log("Unable to launch instance")
raise ValueError("Unable to launch instance") raise ValueError("Unable to launch instance")
self.log("Waiting for instance creation..") self._waitInstanceCreation(self.instance_title, hateoas)
self._waitInstanceCreation(self.instance_title) computer = self.slapos_communicator._hateoas_getComputer("COMP-2732")
#self.log("COMPUTER: " + str(computer))
#software_installation_list = self.slapos_communicator.getSoftwareInstallationList()
#self.log("software_installation_list: ")
#self.log(str(software_installation_list))
getSoftwareInstallationNews = self.slapos_communicator.getSoftwareInstallationNews()
self.log("Software installation news: ")
self.log(str(getSoftwareInstallationNews))
#getInstanceUrlList = self.slapos_communicator.getInstanceUrlList()
#self.log("getInstanceUrlList")
#self.log(str(getInstanceUrlList))
# ROQUE: for debugging purposes
instance_url = 'https://api.vifib.com/software_instance_module/20170822-1568166F/ERP5Document_getHateoas'
self.log("getNewsFromInstance(url)")
self.log(str(self.slapos_communicator.getNewsFromInstance(instance_url)))
self.log("getInformationFromInstance(url)")
self.log(str(self.slapos_communicator.getInformationFromInstance(instance_url)))
# ROQUE: get info contains the software release url
#getInfo = self.slapos_communicator.getInfo()
#self.log("getInfo: ")
#self.log(str(getInfo))
# "No message"
#getFormatedLastMessage = self.slapos_communicator.getFormatedLastMessage()
#self.log("getFormatedLastMessage: ")
#self.log(str(getFormatedLastMessage))
getSoftwareState = self.slapos_communicator._getSoftwareState()
self.log("Software state: " + str(getSoftwareState))
getInstanceState = self.slapos_communicator._getInstanceState()
self.log("Instance state: " + str(getInstanceState))
return {'status_code' : 0} return {'status_code' : 0}
return {'status_code' : 1} return {'status_code' : 1}
...@@ -380,17 +471,21 @@ late a SlapOS (positive) answer." %(str(os.getpid()),str(os.getpid()),)) ...@@ -380,17 +471,21 @@ late a SlapOS (positive) answer." %(str(os.getpid()),str(os.getpid()),))
configuration_list = node_test_suite.configuration_list configuration_list = node_test_suite.configuration_list
test_list = range(0, len(configuration_list)) test_list = range(0, len(configuration_list))
# create test_result # create test_result
self.log("Creating Test Result...")
test_result_proxy = self.testnode.portal.createTestResult( test_result_proxy = self.testnode.portal.createTestResult(
node_test_suite.revision, test_list, node_test_suite.revision, test_list,
self.testnode.config['test_node_title'], self.testnode.config['test_node_title'],
True, node_test_suite.test_suite_title, True, node_test_suite.test_suite_title,
node_test_suite.project_title) node_test_suite.project_title)
self.log("Test Result created.")
count = 0 count = 0
error_message = None error_message = None
# Each cluster configuration are tested # Each cluster configuration are tested
self.log("FOR CONFIG IN CONFIG_LIST:")
for configuration in configuration_list: for configuration in configuration_list:
self.log("configuration n: " + str(count))
self.log(str(configuration))
# First configuration doesn't need XML configuration update. # First configuration doesn't need XML configuration update.
if count > 0: if count > 0:
...@@ -400,19 +495,21 @@ late a SlapOS (positive) answer." %(str(os.getpid()),str(os.getpid()),)) ...@@ -400,19 +495,21 @@ late a SlapOS (positive) answer." %(str(os.getpid()),str(os.getpid()),))
# Update instance XML configuration # Update instance XML configuration
self._updateInstanceXML(configuration, self.instance_title, self._updateInstanceXML(configuration, self.instance_title,
node_test_suite.test_result, node_test_suite.test_suite) node_test_suite.test_result, node_test_suite.test_suite)
self._waitInstance(self.instance_title, 'started') self._waitInstance(self.instance_title, SlapOSMasterCommunicator.INSTANCE_STATE_STARTED)
# Start instance # Start instance
self.slapos_controler.startInstance(self.instance_title) self.slapos_controler.startInstance(self.instance_title)
# XXX: Dirty hack used to force haproxy to restart in time # XXX: Dirty hack used to force haproxy to restart in time
# with all zope informations. # with all zope informations.
self._waitInstance(self.instance_title, 'started') self._waitInstance(self.instance_title, SlapOSMasterCommunicator.INSTANCE_STATE_STARTED)
self.slapos_controler.stopInstance(self.instance_title) #self.slapos_controler.stopInstance(self.instance_title)
self._waitInstance(self.instance_title, 'stopped') self.slapos_communicator._request('stopped')
self.slapos_controler.startInstance(self.instance_title) self._waitInstance(self.instance_title, SlapOSMasterCommunicator.INSTANCE_STATE_STOPPED)
self.slapos_communicator._request('started')
#self.slapos_controler.startInstance(self.instance_title)
########################################################## ##########################################################
self._waitInstance(self.instance_title, 'started') self._waitInstance(self.instance_title, SlapOSMasterCommunicator.INSTANCE_STATE_STARTED)
# Start only the current test # Start only the current test
exclude_list=[x for x in test_list if x!=test_list[count]] exclude_list=[x for x in test_list if x!=test_list[count]]
......
...@@ -37,6 +37,8 @@ import argparse ...@@ -37,6 +37,8 @@ import argparse
import json import json
from slapos import client from slapos import client
from ConfigParser import ConfigParser
MAX_PARTIONS = 10 MAX_PARTIONS = 10
MAX_SR_RETRIES = 3 MAX_SR_RETRIES = 3
...@@ -78,7 +80,7 @@ class SlapOSControler(object): ...@@ -78,7 +80,7 @@ class SlapOSControler(object):
#TODO: implement a method to get all instance related the slapOS account #TODO: implement a method to get all instance related the slapOS account
# and deleting all old instances (based on creation date or name etc...) # and deleting all old instances (based on creation date or name etc...)
def createSlaposConfigurationFileAccount(self, key, certificate, slapos_url, config): def createSlaposConfigurationFileAccount(self, key, certificate, slapos_url, config, slapos_rest_url):
# Create "slapos_account" directory in the "slapos_directory" # Create "slapos_account" directory in the "slapos_directory"
slapos_account_directory = os.path.join(config['slapos_directory'], "slapos_account") slapos_account_directory = os.path.join(config['slapos_directory'], "slapos_account")
createFolder(slapos_account_directory) createFolder(slapos_account_directory)
...@@ -86,9 +88,10 @@ class SlapOSControler(object): ...@@ -86,9 +88,10 @@ class SlapOSControler(object):
slapos_account_key_path = os.path.join(slapos_account_directory, "key") slapos_account_key_path = os.path.join(slapos_account_directory, "key")
slapos_account_certificate_path = os.path.join(slapos_account_directory, "certificate") slapos_account_certificate_path = os.path.join(slapos_account_directory, "certificate")
configuration_file_path = os.path.join(slapos_account_directory, "slapos.cfg") configuration_file_path = os.path.join(slapos_account_directory, "slapos.cfg")
configuration_file_value = "[slapos]\nmaster_url = %s\n\ configuration_file_value = "[slapos]\nmaster_url = %s\nmaster_rest_url = %s\n\
[slapconsole]\ncert_file = %s\nkey_file = %s" %( [slapconsole]\ncert_file = %s\nkey_file = %s" %(
slapos_url, slapos_url,
slapos_rest_url,
slapos_account_certificate_path, slapos_account_certificate_path,
slapos_account_key_path) slapos_account_key_path)
createFile(slapos_account_key_path, "w", key) createFile(slapos_account_key_path, "w", key)
...@@ -109,11 +112,19 @@ class SlapOSControler(object): ...@@ -109,11 +112,19 @@ class SlapOSControler(object):
parser.add_argument("software_url") parser.add_argument("software_url")
parser.add_argument("node") parser.add_argument("node")
if os.path.exists(self.configuration_file_path): if os.path.exists(self.configuration_file_path):
self.log("configuration_file_path: " + str(self.configuration_file_path))
args = parser.parse_args([self.configuration_file_path, software_url, computer_id]) args = parser.parse_args([self.configuration_file_path, software_url, computer_id])
config = client.Config() self.log("parsed args: " + str(args))
config.setConfig(args, args.configuration_file) #config = client.Config()
#config.setConfig(args, args.configuration_file)
self.log("Creating clientConfig...")
configp = ConfigParser()
configp.read(self.configuration_file_path)
config = client.ClientConfig(args, configp)
try: try:
local = client.init(config) self.log("config instantiated. Calling client.init(config)...")
local = client.init(config, self.log)
self.log("local instantiated with client.init!")
local['supply'](software_url, computer_guid=computer_id, state=state) local['supply'](software_url, computer_guid=computer_id, state=state)
self.log('SlapOSControler : supply %s %s %s' %(software_url, computer_id, state)) self.log('SlapOSControler : supply %s %s %s' %(software_url, computer_id, state))
except: except:
......
import datetime
import json import json
import httplib import sys
import urlparse import traceback
import time import time
#import feedparser
from uritemplate import expand
TIMEOUT = 30 import slapos.slap
from slapos.slap import SoftwareProductCollection
from slapos.slap.slap import ConnectionError
from requests.exceptions import HTTPError
from erp5.util.taskdistribution import SAFE_RPC_EXCEPTION_LIST
SOFTWARE_PRODUCT_NAMESPACE = "product."
SOFTWARE_STATE_UNKNOWN = "SOFTWARE_STATE_UNKNOWN"
SOFTWARE_STATE_INSTALLING = "SOFTWARE_STATE_INSTALLING"
SOFTWARE_STATE_INSTALLED = "SOFTWARE_STATE_INSTALLED"
SOFTWARE_STATE_DESTROYING = "SOFTWARE_STATE_DESTROYING"
INSTANCE_STATE_UNKNOWN = "INSTANCE_STATE_UNKNOWN"
INSTANCE_STATE_STARTING = "INSTANCE_STATE_STARTING"
INSTANCE_STATE_STARTED = "INSTANCE_STATE_STARTED"
INSTANCE_STATE_STARTED_WITH_ERROR = "INSTANCE_STATE_STARTED_WITH_ERROR"
INSTANCE_STATE_STOPPING = "INSTANCE_STATE_STOPPING"
INSTANCE_STATE_STOPPED = "INSTANCE_STATE_STOPPED"
INSTANCE_STATE_DESTROYING = "INSTANCE_STATE_DESTROYING"
TESTER_STATE_INITIAL = "TESTER_STATE_INITIAL"
TESTER_STATE_NOTHING = "TESTER_STATE_NOTHING"
TESTER_STATE_SOFTWARE_INSTALLED = "TESTER_STATE_SOFTWARE_INSTALLED"
TESTER_STATE_INSTANCE_INSTALLED = "TESTER_STATE_INSTANCE_INSTALLED"
TESTER_STATE_INSTANCE_STARTED = "TESTER_STATE_INSTANCE_STARTED"
TESTER_STATE_INSTANCE_UNINSTALLED = "TESTER_STATE_INSTANCE_UNINSTALLED"
# Simple decorator to prevent raise due small
# network failures.
def retryOnNetworkFailure(func):
def wrapper(*args, **kwargs):
retry_time = 64
while True:
try:
return func(*args, **kwargs)
except SAFE_RPC_EXCEPTION_LIST, e:
print 'Network failure: %s , %s' % (sys.exc_info(), e)
except HTTPError, e:
print 'Network failure: %s , %s' % (sys.exc_info(), e)
except ConnectionError, e:
print 'Network failure: %s , %s' % (sys.exc_info(), e)
except slapos.slap.ConnectionError, e:
print 'Network failure: %s , %s' % (sys.exc_info(), e)
print 'Retry method %s in %i seconds' % (func, retry_time)
time.sleep(retry_time)
retry_time = min(retry_time*1.5, 640)
wrapper.__name__ = func.__name__
wrapper.__doc__ = func.__doc__
return wrapper
# TODO: News-> look list to get last news... (and not the first of the list)
class SlapOSMasterCommunicator(object): class SlapOSMasterCommunicator(object):
"""
Communication with slapos Master using Hateoas.
def __init__(self, slap, slap_supply, slap_order, url, logger ):
collection: collection of data (hosting_subscription, instance, software_release...)
hosting_subscription: result of a request self._logger = logger
instance(s): instance(s) related to an hosting_subscription self.slap = slap
self.slap_order = slap_order
usage: ex: self.slap_supply = slap_supply
# Try to reuse same communicator, because initilization step may takes a lot of time self.hateoas_navigator = self.slap._hateoas_navigator
# due to listing of all instances (alive or not) related to the specified slapOS account. self.hosting_subscription_url = None
communicator = SlapOSMasterCommunicator()
#self.computer_guid = computer_guid
# Print news related to 'TestScalability_21423104630420' all instances #self.name = name
instance_link_list = communicator._getRelatedInstanceLink('TestScalability_21423104630420')
for instance_link in instance_link_list: if url is not None and \
news = communicator.getNewsFromInstanceLink(instance_link) url.startswith(SOFTWARE_PRODUCT_NAMESPACE):
print news['news']
""" product = SoftwareProductCollection(self._logger, self.slap)
def __init__(self, certificate_path, key_path, log, try:
url): url = product.__getattr__(url[len(SOFTWARE_PRODUCT_NAMESPACE):])
# Create connection except AttributeError as e:
api_scheme, api_netloc, api_path, api_query, api_fragment = urlparse.urlsplit(url) self._logger.warning('Error on get software release : %s ' % e.message)
self.log = log
self.certificate_path = certificate_path
self.key_path = key_path
self.url = url self.url = url
self.log("Getting connection to " + str(self.url))
self.connection = self._getConnection(self.certificate_path, self.key_path, self.url)
self.log("Connection done.")
# Get master
master_link = {'href':api_path,'type':"application/vnd.slapos.org.hal+json; class=slapos.org.master"}
self.log("Getting master with href: " + str(api_path))
#master = self._curl(master_link)
#self.person_link = master['_links']['http://slapos.org/reg/me']
self.person_link = { "href": "urn:jio:get:person_module/20170705-995F2E" }
self.log("Getting person with curl to harcoded link.")
# Get person related to specified key/certificate provided
person = self._curl(self.person_link)
self.log("person: " str(person))
self.personnal_collection_link = person['_links']['http://slapos.org/reg/hosting_subscription']
self.log("personal collection link : " + str(self.personnal_collection_link))
# Get collection (of hosting subscriptions)
self.log("Getting collection with curl")
collection = self._curl(self.personnal_collection_link)
self.log("collection: " + str(collection))
# XXX: This part may be extremly long (because here no hosting subscriptions
# has been visited)
self.hosting_subcriptions_dict = {}
self.visited_hosting_subcriptions_link_list = []
self.log("SlapOSMasterCommunicator will read all hosting subscriptions entries, "
"it may take several time...")
self._update_hosting_subscription_informations()
def _getConnection(self,certificate_path, key_path, url):
api_scheme, api_netloc, api_path, api_query, api_fragment = urlparse.urlsplit(url)
#self.log("HTTPS Connection with: api_scheme=%s, api_netloc=%s, api_path=%s, api_query=%s, api_fragment=%s" %(api_scheme, api_netloc, api_path, api_query, api_fragment))
self.log("_getConnection method - api_netloc: " + str(api_netloc))
return httplib.HTTPSConnection(api_netloc, key_file=key_path, cert_file=certificate_path, timeout=TIMEOUT)
def _curl(self, link):
"""
'link' must look like : {'href':url,'type':content_type}
"""
# Set timeout
import socket
socket.setdefaulttimeout(1.0*TIMEOUT)
api_scheme, api_netloc, api_path, api_query, api_fragment = urlparse.urlsplit(link['href']) def setName(self, name):
max_retry = 10 self.name = name
# Try to use existing conection
def setRequestParameters(self, request_kw):
if isinstance(request_kw, str) or \
isinstance(request_kw, unicode):
self.request_kw = json.loads(request_kw)
else:
self.request_kw = request_kw
#self._logger("request parameters set: " + str(self.request_kw))
@retryOnNetworkFailure
def _supply(self, state):
if self.computer_guid is None:
self._logger.log('Nothing to supply for %s.' % (self.name))
return None
self._logger("From SlapOSMasterCommunicator")
self._logger('Supply %s@%s: %s', self.url, self.computer_guid,
state)
return self.slap_supply.supply(self.url, self.computer_guid)
#return self.slap_supply.supply(self.url, self.computer_guid, state)
@retryOnNetworkFailure
def _request(self, state):
self._logger.info('Request %s@%s: %s', self.url, self.name, state)
self.latest_state = state
return self.slap_order.request(
software_release=self.url,
partition_reference=self.name,
state=state,
**self.request_kw)
@retryOnNetworkFailure
def _hateoas_getComputer(self, reference):
root_document = self.hateoas_navigator.getRootDocument()
search_url = root_document["_links"]['raw_search']['href']
getter_link = expand(search_url, {
"query": "reference:%s AND portal_type:Computer" % reference,
"select_list": ["relative_url"],
"limit": 1})
result = self.hateoas_navigator.GET(getter_link)
content_list = json.loads(result)['_embedded']['contents']
if len(content_list) == 0:
raise Exception('No Computer found.')
computer_relative_url = content_list[0]["relative_url"]
getter_url = self.hateoas_navigator.getDocumentAndHateoas(
computer_relative_url)
return json.loads(self.hateoas_navigator.GET(getter_url))
@retryOnNetworkFailure
def getSoftwareInstallationList(self):
# XXX Move me to slap.py API
computer = self._hateoas_getComputer(self.computer_guid)
# Not a list ?
action = computer['_links']['action_object_slap']
if action.get('title') == 'getHateoasSoftwareInstallationList':
getter_link = action['href']
else:
raise Exception('No Link found found.')
result = self.hateoas_navigator.GET(getter_link)
return json.loads(result)['_links']['content']
@retryOnNetworkFailure
def getSoftwareInstallationNews(self):
getter_link = None
for si in self.getSoftwareInstallationList():
if si["title"] == self.url:
getter_link = si["href"]
break
# We could not find the document, so it is probably too soon.
if getter_link is None:
return ""
result = self.hateoas_navigator.GET(getter_link)
action_object_slap_list = json.loads(result)['_links']['action_object_slap']
for action in action_object_slap_list:
if action.get('title') == 'getHateoasNews':
getter_link = action['href']
break
else:
raise Exception('getHateoasNews not found.')
result = self.hateoas_navigator.GET(getter_link)
if len(json.loads(result)['news']) > 0:
return json.loads(result)['news'][0]["text"]
return ""
@retryOnNetworkFailure
def getInstanceUrlList(self):
if self.hosting_subscription_url is None:
hosting_subscription_dict = self.hateoas_navigator._hateoas_getHostingSubscriptionDict()
for hs in hosting_subscription_dict:
if hs['title'] == self.name:
self.hosting_subscription_url = hs['href']
break
if self.hosting_subscription_url is None:
return None
return self.hateoas_navigator.getHateoasInstanceList(
self.hosting_subscription_url)
@retryOnNetworkFailure
def getNewsFromInstance(self, url):
result = self.hateoas_navigator.GET(url)
result = json.loads(result)
if result['_links'].get('action_object_slap', None) is None:
return None
object_link = self.hateoas_navigator.hateoasGetLinkFromLinks(
result['_links']['action_object_slap'], 'getHateoasNews')
result = self.hateoas_navigator.GET(object_link)
return json.loads(result)['news']
@retryOnNetworkFailure
def getInformationFromInstance(self, url):
result = self.hateoas_navigator.GET(url)
result = json.loads(result)
if result['_links'].get('action_object_slap', None) is None:
print result['links']
return None
object_link = self.hateoas_navigator.hateoasGetLinkFromLinks(
result['_links']['action_object_slap'], 'getHateoasInformation')
result = self.hateoas_navigator.GET(object_link)
return json.loads(result)
class SoftwareReleaseTester(SlapOSMasterCommunicator):
deadline = None
latest_state = None
def __init__(self,
name,
logger,
slap,
slap_order,
slap_supply,
url, # software release url
computer_guid=None, # computer for supply if desired
request_kw=None, # instance parameters, if instantiation
# testing is desired
software_timeout=3600,
instance_timeout=3600,
):
super(SoftwareReleaseTester, self).__init__(
slap, slap_supply, slap_order, url, logger)
self.name = name
self.computer_guid = computer_guid
if isinstance(request_kw, str) or \
isinstance(request_kw, unicode):
self.request_kw = json.loads(request_kw)
else:
self.request_kw = request_kw
self.message_history = []
self.state = TESTER_STATE_INITIAL
self.transition_dict = {
# step function
# delay
# next_state
# software_state
# instance_state
TESTER_STATE_INITIAL: (
lambda t: None,
None,
TESTER_STATE_NOTHING,
None,
None,
),
TESTER_STATE_NOTHING: (
lambda t: t._supply("available"),
int(software_timeout),
request_kw is None and TESTER_STATE_INSTANCE_UNINSTALLED or \
TESTER_STATE_SOFTWARE_INSTALLED,
SOFTWARE_STATE_INSTALLED,
None,
),
TESTER_STATE_SOFTWARE_INSTALLED: (
lambda t: t._request("started"),
int(instance_timeout),
TESTER_STATE_INSTANCE_STARTED,
None,
INSTANCE_STATE_STARTED,
),
TESTER_STATE_INSTANCE_STARTED: (
lambda t: t._request("destroyed"),
int(1200),
TESTER_STATE_INSTANCE_UNINSTALLED,
None,
INSTANCE_STATE_STOPPED,
),
TESTER_STATE_INSTANCE_UNINSTALLED: (
lambda t: t._supply("destroyed"),
int(1200),
None,
None,
None,
),
}
def __repr__(self):
deadline = self.deadline
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))
# ROQUE: this method is a hack. This set must occur during request.
def forceSetState(self, state):
self.latest_state = state
def getInfo(self):
info = ""
info += "Software Release URL: %s\n" % (self.url)
if self.computer_guid is not None:
info += "Supply requested on: %s\n" % (self.computer_guid)
info += "Instance Requested (Parameters): %s\n" % self.request_kw
return info
def getFormatedLastMessage(self):
if len(self.message_history) == 0:
return "No message"
summary = "Summary about the test. Instance List and Status:\n"
message = "Last information about the tester:\n"
if self.message_history[-1] is not None:
message_list = self.message_history[-1]
for entry in message_list:
summary += "%s %s -> %s\n" % (
entry['title'], entry["slave"] and "(slave)" or "", entry['state'])
for prop in entry:
if prop != "information":
message += "%s = %s\n" % (prop, json.dumps(entry[prop], indent=2))
message += "=== connection_dict === \n%s\n" % (
json.dumps(entry["information"]["connection_dict"], indent=2))
message += "\n"
message += "=== parameter_dict === \n%s\n" % (
json.dumps(entry["information"]["parameter_dict"], indent=2))
message += "\n"
message += "="*79
message += "\n\n\n"
return summary + message
def _getSoftwareState(self):
if self.computer_guid is None:
return SOFTWARE_STATE_INSTALLED
message = self.getSoftwareInstallationNews()
if message.startswith("#error no data found"):
return SOFTWARE_STATE_UNKNOWN
if message.startswith('#access software release'):
return SOFTWARE_STATE_INSTALLED
if message.startswith('#error'):
return SOFTWARE_STATE_INSTALLING
return SOFTWARE_STATE_UNKNOWN
@retryOnNetworkFailure
def getRSSEntryFromMonitoring(self, base_url):
if base_url is None:
return {}
feed_url = base_url + '/monitor-public/rssfeed.html'
d = feedparser.parse(feed_url)
if len(d.entries) > 0:
return {"date": d.entries[0].published,
"message": d.entries[0].description,
"title" : d.entries[0].title}
return {}
@retryOnNetworkFailure
def _getInstanceState(self):
latest_state = self.latest_state
self._logger('latest_state = %r', latest_state)
if latest_state is None:
return INSTANCE_STATE_UNKNOWN
message_list = []
try: try:
self.log("Curl to link: " + str(link)) for instance in self.getInstanceUrlList():
self.log("GET to api " + str(api_path)) news = self.getNewsFromInstance(instance["href"])
self.connection.request(method='GET', url=api_path, body="") information = self.getInformationFromInstance(instance["href"])
self.log("Request done. Getting response...") state = INSTANCE_STATE_UNKNOWN
response = self.connection.getresponse() monitor_information_dict = {}
self.log("REPONSE.read: " + str(response.read()))
return json.loads(response.read()) info_created_at = "-1"
# Create and use new connection is_slave = information['slave']
except: if is_slave:
self.log("Request or response fail! Entering the while-try") self._logger.debug('Instance is slave')
retry = 0 if (information["connection_dict"]) > 0:
# (re)Try several time to use new connection state = INSTANCE_STATE_STARTED
while retry < max_retry: else:
# not slave
instance_state = news[0]
if instance_state.get('created_at', '-1') != "-1":
# the following does NOT take TZ into account
created_at = datetime.datetime.strptime(instance_state['created_at'],
'%a, %d %b %Y %H:%M:%S %Z')
gmt_now = datetime.datetime(*time.gmtime()[:6])
info_created_at = '%s (%d)' % (
instance_state['created_at'], (gmt_now - created_at).seconds)
if instance_state['text'].startswith('#access'):
state = INSTANCE_STATE_STARTED
if instance_state['text'].startswith('#access Instance correctly stopped'):
state = INSTANCE_STATE_STOPPED
if instance_state['text'].startswith('#error'):
state = INSTANCE_STATE_STARTED_WITH_ERROR
if state == INSTANCE_STATE_STARTED_WITH_ERROR:
# search for monitor url
monitor_v6_url = information["connection_dict"].get("monitor_v6_url")
try: try:
self.log("Retry number: " + str(retry)) monitor_information_dict = self.getRSSEntryFromMonitoring(monitor_v6_url)
self.log("entering _getConnection() to " + str(self.url)) self._logger("[DEBUG] monitor information dictionary: ")
self.connection = self._getConnection(self.certificate_path, self.key_path, self.url) self._logger(str(monitor_information_dict))
self.log("_getConnection DONE. Getting response...") except Exception:
self.connection.request(method='GET', url=api_path, headers={'Accept': link['type']}, body="") self._logger.exception('Unable to download promises for: %s' % (instance["title"]))
response = self.connection.getresponse() self._logger.info(traceback.format_exc())
self.log("REPONSE.read: " + str(response.read())) monitor_information_dict = {"message": "Unable to download"}
return json.loads(response.read())
except Exception, e:
self.log("SlapOSMasterCommunicator: Connection failed..")
self.log("EXCEPTION MESSAGE: "+ str(e))
retry += 1
time.sleep(10)
self.log("SlapOSMasterCommunicator: All connection attempts failed after %d try.." %max_retry)
raise ValueError("SlapOSMasterCommunicator: Impossible to use connection")
def _update_hosting_subscription_informations(self):
"""
Add all not already visited hosting_subcription
# Visit all hosting subscriptions and fill a dict containing all
# new hosting subscriptions. ( like: {hs1_title:hs1_link, hs2_title:hs2_link, ..} )
# and a list of visited hosting_subsciption ( like: [hs1_link, hs2_link, ..] )
"""
collection = self._curl(self.personnal_collection_link)
# For each hosting_subcription present in the collection
for hosting_subscription_link in collection['_links']['item']:
if hosting_subscription_link not in self.visited_hosting_subcriptions_link_list:
hosting_subscription = self._curl(hosting_subscription_link)
self.hosting_subcriptions_dict.update({hosting_subscription['title']:hosting_subscription_link})
self.visited_hosting_subcriptions_link_list.append(hosting_subscription_link)
def _getRelatedInstanceLink(self, hosting_subscription_title):
"""
Return a list of all related instance_url from an hosting_subscription_title
"""
# Update informations
self._update_hosting_subscription_informations()
# Get specified hosting_subscription
hosting_subscription_link = self.hosting_subcriptions_dict[hosting_subscription_title]
hosting_subscription = self._curl(hosting_subscription_link)
assert(hosting_subscription_title == hosting_subscription['title'])
# Get instance collection related to this hosting_subscription
instance_collection_link = hosting_subscription['_links']['http://slapos.org/reg/instance']
instance_collection = self._curl(instance_collection_link)
related_instance_link_list = []
# For each instance present in the collection
for instance in instance_collection['_links']['item']:
related_instance_link_list.append(instance)
return related_instance_link_list
def getNewsFromInstanceLink(self, instance_link):
instance = self._curl(instance_link)
news_link = instance['_links']['http://slapos.org/reg/news']
return self._curl(news_link)
def isHostingSubsciptionStatusEqualTo(self, hosting_subscription_title, excepted_news_text):
"""
Return True if all related instance state are equal to status,
or False if not or if there is are no related instances.
"""
related_instance_link_list = _getRelatedInstanceLink(hosting_subscription_title)
# For each instance present in the collection
for instance_link in related_instance_link_list:
news = self.getNewsFromInstanceLink(instance_link)
if excepted_news_text != news['news'][0]['text']:
return False
return len(related_instance_link_list) > 0
def isInstanceReady(self, instance_link, status):
"""
Return True if instance status and instance news text ~looks corresponding.
( use the matching of 'correctly' and 'Instance' and status )
"""
# XXX: SlapOS Master doesn't store any "news" about slave instances. Assume true.
if self._curl(instance_link)['slave']:
return True
text = self.getNewsFromInstanceLink(instance_link)['news'][0]['text']
return ('Instance' in text) and ('correctly' in text) and (status in text)
# check if provided 'status' = status
def isHostingSubscriptionReady(self, hosting_subscription_title, status):
"""
Return True if all instance status and instance news text ~looks corresponding.
( use the matching of 'correctly' and 'Instance' and status ).
"""
instance_link_list = self._getRelatedInstanceLink(hosting_subscription_title)
for instance_link in instance_link_list:
if not self.isInstanceReady(instance_link, status):
return False
return len(instance_link_list) > 0
def isRegisteredHostingSubscription(self, hosting_subscription_title): self._logger ('Instance state: %s -> %s' % (instance['title'], state))
""" self._logger ('Instance Created at: %s -> %s' % (instance['title'], info_created_at))
Return True if the specified hosting_subscription is present on SlapOSMaster
"""
self._update_hosting_subscription_informations()
if self.hosting_subcriptions_dict.get(hosting_subscription_title):
return True
return False
def getHostingSubscriptionDict(self): message_list.append({
'title': instance["title"],
'slave': is_slave,
'news': news[0],
'information': information,
'monitor': monitor_information_dict,
'state': state
})
except slapos.slap.ServerError:
self._logger.exception('Got an error requesting partition for '
'its state')
return INSTANCE_STATE_UNKNOWN
started = 0
stopped = 0
self.message_history.append(message_list)
for instance in message_list:
if not instance['slave'] and \
instance['state'] in (INSTANCE_STATE_UNKNOWN, INSTANCE_STATE_STARTED_WITH_ERROR):
return instance['state']
elif not instance['slave'] and instance['state'] == INSTANCE_STATE_STARTED:
started = 1
elif not instance['slave'] and instance['state'] == INSTANCE_STATE_STOPPED:
stopped = 1
if instance['slave'] and instance['state'] == INSTANCE_STATE_UNKNOWN:
return instance['state']
if started and stopped:
return INSTANCE_STATE_UNKNOWN
if started:
return INSTANCE_STATE_STARTED
if stopped:
return INSTANCE_STATE_STOPPED
@retryOnNetworkFailure
def teardown(self):
""" """
Return the dict of hosting subcription. Interrupt a running test sequence, putting it in idle state.
""" """
return self.hosting_subcriptions_dict self._logger.info('Invoking TearDown for %s@%s' % (self.url, self.name))
if self.request_kw is not None:
self._request('destroyed')
if self.computer_guid is not None:
self._supply('destroyed')
self.state = TESTER_STATE_INSTANCE_UNINSTALLED
def getHostingSubscriptionInformationDict(self, title): def tic(self, now):
""" """
Return a dict with informations about Hosting subscription Check for missed deadlines (-> test failure), conditions for moving to
next state, and actually moving to next state (executing its payload).
""" """
related_instance_link_list = self._getRelatedInstanceLink(title) self._logger.debug('TIC')
related_instance_link = None deadline = self.deadline
# Get root instance
for link in related_instance_link_list: if deadline < now and deadline is not None:
instance = self._curl(link) raise TestTimeout(self.state)
if title == instance['title']:
related_instance_link = link _, _, next_state, software_state, instance_state = self.transition_dict[
break self.state]
# Return information dict
if related_instance_link: if (software_state is None or
related_instance = self._curl(related_instance_link) software_state == self._getSoftwareState()) and (
return { instance_state is None or
'title': related_instance['title'], instance_state == self._getInstanceState()):
'status': related_instance['status'],
'software_url': related_instance['_links']['http://slapos.org/reg/release'], self._logger.debug('Going to state %s (%r)', next_state, instance_state)
'software_type': related_instance['software_type'], if next_state is None:
'computer_guid': related_instance['sla']['computer_guid']
}
else:
return None return None
self.state = next_state
stepfunc, delay, _, _, _ = self.transition_dict[next_state]
self.deadline = now + delay
stepfunc(self)
return self.deadline
...@@ -65,6 +65,7 @@ class TestNode(object): ...@@ -65,6 +65,7 @@ class TestNode(object):
max_temp_time=MAX_TEMP_TIME): max_temp_time=MAX_TEMP_TIME):
self.testnode_log = log self.testnode_log = log
self.log = log self.log = log
self.log("Config parameter in TestNode.init(): " + str(config))
self.config = config or {} self.config = config or {}
self.process_manager = ProcessManager(log) self.process_manager = ProcessManager(log)
self.working_directory = config['working_directory'] self.working_directory = config['working_directory']
...@@ -405,9 +406,14 @@ shared = true ...@@ -405,9 +406,14 @@ shared = true
node_test_suite.edit(test_result=test_result) node_test_suite.edit(test_result=test_result)
# get cluster configuration for this test suite, this is needed to # get cluster configuration for this test suite, this is needed to
# know slapos parameters to user for creating instances # know slapos parameters to user for creating instances
node_test_suite.edit(cluster_configuration=Utils.deunicodeData(
json.loads(self.test_suite_portal.generateConfiguration( log("Getting configuration from test suite " + str(node_test_suite.test_suite_title))
node_test_suite.test_suite_title))['configuration_list'][0])) generated_config = self.test_suite_portal.generateConfiguration(node_test_suite.test_suite_title)
log("Generated configuration: " + str(generated_config))
jsonData = json.loads(generated_config)
cluster_configuration = Utils.deunicodeData(jsonData['configuration_list'][0])
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
status_dict = runner.prepareSlapOSForTestSuite(node_test_suite) status_dict = runner.prepareSlapOSForTestSuite(node_test_suite)
# Give some time so computer partitions may start # Give some time so computer partitions may start
......
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