Commit 90a7f4a7 authored by Cédric Le Ninivin's avatar Cédric Le Ninivin

slapos: Update slapgrid to use new API

parent 8652fba3
...@@ -423,6 +423,7 @@ class Partition(object): ...@@ -423,6 +423,7 @@ class Partition(object):
instance_storage_home='', instance_storage_home='',
ipv4_global_network='', ipv4_global_network='',
buildout_debug=False, buildout_debug=False,
api_backward_compatibility=False,
): ):
"""Initialisation of class parameters""" """Initialisation of class parameters"""
self.buildout = buildout self.buildout = buildout
...@@ -443,6 +444,7 @@ class Partition(object): ...@@ -443,6 +444,7 @@ class Partition(object):
self.software_release_url = software_release_url self.software_release_url = software_release_url
self.instance_storage_home = instance_storage_home self.instance_storage_home = instance_storage_home
self.ipv4_global_network = ipv4_global_network self.ipv4_global_network = ipv4_global_network
self.api_backward_compatibility = api_backward_compatibility
self.key_file = '' self.key_file = ''
self.cert_file = '' self.cert_file = ''
...@@ -489,16 +491,20 @@ class Partition(object): ...@@ -489,16 +491,20 @@ class Partition(object):
# Certificate files are unset, skip. # Certificate files are unset, skip.
return return
try: if self.api_backward_compatibility:
partition_certificate = self.computer_partition.getCertificate() try:
except NotFoundError: partition_certificate = self.computer_partition["slap_partition"].getCertificate()
raise NotFoundError('Partition %s is not known by SlapOS Master.' % self.computer_partition["X509"] = {}
self.partition_id) self.computer_partition["X509"]["certificate"] = partition_certificate["certificate"]
self.computer_partition["X509"]["key"] = partition_certificate["key"]
except NotFoundError:
raise NotFoundError('Partition %s is not known by SlapOS Master.' %
self.partition_id)
uid, gid = self.getUserGroupId() uid, gid = self.getUserGroupId()
for name, path in [('certificate', self.cert_file), ('key', self.key_file)]: for name, path in [('certificate', self.cert_file), ('key', self.key_file)]:
new_content = partition_certificate[name] new_content = self.computer_partition["X509"][name]
old_content = None old_content = None
if os.path.exists(path): if os.path.exists(path):
old_content = open(path).read() old_content = open(path).read()
...@@ -582,7 +588,7 @@ class Partition(object): ...@@ -582,7 +588,7 @@ class Partition(object):
installs the software partition with the help of buildout installs the software partition with the help of buildout
""" """
self.logger.info("Installing Computer Partition %s..." self.logger.info("Installing Computer Partition %s..."
% self.computer_partition.getId()) % self.computer_partition.get("compute_partition_id"))
self.check_free_space() self.check_free_space()
...@@ -731,7 +737,7 @@ class Partition(object): ...@@ -731,7 +737,7 @@ class Partition(object):
if os.path.exists(self.supervisord_partition_configuration_path): if os.path.exists(self.supervisord_partition_configuration_path):
os.unlink(self.supervisord_partition_configuration_path) os.unlink(self.supervisord_partition_configuration_path)
else: else:
partition_id = self.computer_partition.getId() partition_id = self.computer_partition.get("compute_partition_id")
group_partition_template = bytes2str(pkg_resources.resource_string(__name__, group_partition_template = bytes2str(pkg_resources.resource_string(__name__,
'templates/group_partition_supervisord.conf.in')) 'templates/group_partition_supervisord.conf.in'))
self.supervisor_configuration_group = group_partition_template % { self.supervisor_configuration_group = group_partition_template % {
...@@ -766,22 +772,22 @@ class Partition(object): ...@@ -766,22 +772,22 @@ class Partition(object):
"""Asks supervisord to start the instance. If this instance is not """Asks supervisord to start the instance. If this instance is not
installed, we install it. installed, we install it.
""" """
partition_id = self.computer_partition.getId() partition_id = self.computer_partition.get("compute_partition_id")
try: try:
with self.getSupervisorRPC() as supervisor: with self.getSupervisorRPC() as supervisor:
supervisor.startProcessGroup(partition_id, False) supervisor.startProcessGroup(partition_id, False)
except xmlrpclib.Fault as exc: except xmlrpclib.Fault as exc:
if exc.faultString.startswith('BAD_NAME:'): if exc.faultString.startswith('BAD_NAME:'):
self.logger.info("Nothing to start on %s..." % self.logger.info("Nothing to start on %s..." %
self.computer_partition.getId()) self.computer_partition.get("compute_partition_id"))
else: else:
raise raise
else: else:
self.logger.info("Requested start of %s..." % self.computer_partition.getId()) self.logger.info("Requested start of %s..." % self.computer_partition.get("compute_partition_id"))
def stop(self): def stop(self):
"""Asks supervisord to stop the instance.""" """Asks supervisord to stop the instance."""
partition_id = self.computer_partition.getId() partition_id = self.computer_partition.get("compute_partition_id")
try: try:
with self.getSupervisorRPC() as supervisor: with self.getSupervisorRPC() as supervisor:
supervisor.stopProcessGroup(partition_id, False) supervisor.stopProcessGroup(partition_id, False)
...@@ -791,13 +797,13 @@ class Partition(object): ...@@ -791,13 +797,13 @@ class Partition(object):
else: else:
raise raise
else: else:
self.logger.info("Requested stop of %s..." % self.computer_partition.getId()) self.logger.info("Requested stop of %s..." % self.computer_partition.get("compute_partition_id"))
def destroy(self): def destroy(self):
"""Destroys the partition and makes it available for subsequent use." """Destroys the partition and makes it available for subsequent use."
""" """
self.logger.info("Destroying Computer Partition %s..." self.logger.info("Destroying Computer Partition %s..."
% self.computer_partition.getId()) % self.computer_partition.get("compute_partition_id"))
self.createRetentionLockDate() self.createRetentionLockDate()
if not self.checkRetentionIsAuthorized(): if not self.checkRetentionIsAuthorized():
......
...@@ -366,6 +366,7 @@ class Slapgrid(object): ...@@ -366,6 +366,7 @@ class Slapgrid(object):
buildout_debug=False, buildout_debug=False,
shared_part_list='', shared_part_list='',
force_stop=False, force_stop=False,
slapgrid_jio_uri=None,
): ):
"""Makes easy initialisation of class parameters""" """Makes easy initialisation of class parameters"""
# Parses arguments # Parses arguments
...@@ -400,10 +401,16 @@ class Slapgrid(object): ...@@ -400,10 +401,16 @@ class Slapgrid(object):
self.shadir_key_file = shadir_key_file self.shadir_key_file = shadir_key_file
self.forbid_supervisord_automatic_launch = forbid_supervisord_automatic_launch self.forbid_supervisord_automatic_launch = forbid_supervisord_automatic_launch
self.logger = logger self.logger = logger
self.slapgrid_jio_uri = slapgrid_jio_uri
# Creates objects from slap module # Creates objects from slap module
self.slap = slapos.slap.slap() self.slap = slapos.slap.slap()
self.slap.initializeConnection(self.master_url, key_file=self.key_file, self.slap.initializeConnection(self.master_url, key_file=self.key_file,
cert_file=self.cert_file, master_ca_file=self.master_ca_file) cert_file=self.cert_file, master_ca_file=self.master_ca_file,
slapgrid_jio_uri=self.slapgrid_jio_uri)
if self.slap.jio_api_connector:
self.api_backward_compatibility = False
else:
self.api_backward_compatibility = True
self.computer = self.slap.registerComputer(self.computer_id) self.computer = self.slap.registerComputer(self.computer_id)
# Defines all needed paths # Defines all needed paths
self.buildout = buildout self.buildout = buildout
...@@ -419,6 +426,7 @@ class Slapgrid(object): ...@@ -419,6 +426,7 @@ class Slapgrid(object):
if computer_partition_filter_list is not None: if computer_partition_filter_list is not None:
self.computer_partition_filter_list = \ self.computer_partition_filter_list = \
computer_partition_filter_list.split(",") computer_partition_filter_list.split(",")
self.computer_partition_list = None
self.maximum_periodicity = maximum_periodicity self.maximum_periodicity = maximum_periodicity
self.software_min_free_space = software_min_free_space self.software_min_free_space = software_min_free_space
self.instance_min_free_space = instance_min_free_space self.instance_min_free_space = instance_min_free_space
...@@ -553,22 +561,62 @@ stderr_logfile_backups=1 ...@@ -553,22 +561,62 @@ stderr_logfile_backups=1
launchSupervisord(instance_root=self.instance_root, logger=self.logger) launchSupervisord(instance_root=self.instance_root, logger=self.logger)
def getComputerPartitionList(self): def getComputerPartitionList(self):
try: if self.computer_partition_list is None:
return self.computer.getComputerPartitionList() if not self.api_backward_compatibility:
except socket.error as exc: self.computer_partition_list = self.slap.jio_api_connector.allDocs({
self.logger.fatal(exc) "portal_type": "Software Instance",
raise "compute_node_id": self.computer_id,
}).get("result_list", [])
else:
try:
slap_partition_list = self.computer.getComputerPartitionList()
except socket.error as exc:
self.logger.fatal(exc)
raise
self.computer_partition_list = []
for partition in slap_partition_list:
try:
software_release_uri = partition.getSoftwareRelease().getURI()
except (NotFoundError, TypeError, NameError):
software_release_uri = None
self.computer_partition_list.append({
"reference": partition._instance_guid,
"portal_type": "Software Instance",
"compute_partition_id": partition.getId(),
"state": partition.getState(),
"software_type": partition.getInstanceParameterDict().get(
'slap_software_type', None),
"parameters": partition.getInstanceParameterDict(),
"instance_processing_timestamp": partition.getInstanceParameterDict().get(
"timestamp"),
"slap_partition": partition,
"access_status_message": partition.getAccessStatus(),
"software_release_uri": software_release_uri,
"sla_parameters": getattr(partition, '_filter_dict', {}),
})
return self.computer_partition_list
def sendPartitionError(self, partition, error_message, logger=None):
if not self.api_backward_compatibility:
self.slap.jio_api_connector.put({
"portal_type": "Software Instance",
"reported_state": "error",
"status_message": str(error_message),
"reference": partition.get("reference")
})
else:
partition["slap_partition"].error(error_message, logger=logger)
def getRequiredComputerPartitionList(self): def getRequiredComputerPartitionList(self):
"""Return the computer partitions that should be processed. """Return the computer partitions that should be processed.
""" """
cp_list = self.getComputerPartitionList() cp_list = self.getComputerPartitionList()
cp_id_list = [cp.getId() for cp in cp_list] cp_id_list = [cp.get("computer_partition_id", "") for cp in cp_list]
required_cp_id_set = check_required_only_partitions( required_cp_id_set = check_required_only_partitions(
cp_id_list, self.computer_partition_filter_list) cp_id_list, self.computer_partition_filter_list)
busy_cp_list = self.FilterComputerPartitionList(cp_list) busy_cp_list = self.FilterComputerPartitionList(cp_list)
if required_cp_id_set: if required_cp_id_set:
return [cp for cp in busy_cp_list if cp.getId() in required_cp_id_set] return [cp for cp in busy_cp_list if cp.get("computer_partition_id", "") in required_cp_id_set]
return busy_cp_list return busy_cp_list
def processSoftwareReleaseList(self): def processSoftwareReleaseList(self):
...@@ -578,10 +626,27 @@ stderr_logfile_backups=1 ...@@ -578,10 +626,27 @@ stderr_logfile_backups=1
self.logger.info('Processing software releases...') self.logger.info('Processing software releases...')
# Boolean to know if every instance has correctly been deployed # Boolean to know if every instance has correctly been deployed
clean_run = True clean_run = True
for software_release in self.computer.getSoftwareReleaseList(): if not self.api_backward_compatibility:
state = software_release.getState() software_installation_list = self.slap.jio_api_connector.allDocs({
"portal_type": "Software Installation",
"compute_node_id": self.computer_id
})
if "result_list" in software_installation_list:
software_installation_list = software_installation_list["result_list"]
else:
software_installation_list = []
else:
software_installation_list = []
for software_release in self.computer.getSoftwareReleaseList():
software_installation_list.append({
"software_release_uri": software_release.getURI(),
"state": software_release.getState(),
"compatibility_software_release": software_release,
})
for software_release in software_installation_list:
state = software_release["state"]
try: try:
software_release_uri = software_release.getURI() software_release_uri = software_release["software_release_uri"]
url_hash = md5digest(software_release_uri) url_hash = md5digest(software_release_uri)
software_path = os.path.join(self.software_root, url_hash) software_path = os.path.join(self.software_root, url_hash)
software = Software(url=software_release_uri, software = Software(url=software_release_uri,
...@@ -623,7 +688,15 @@ stderr_logfile_backups=1 ...@@ -623,7 +688,15 @@ stderr_logfile_backups=1
url_hash in self.software_release_filter_list or url_hash in self.software_release_filter_list or
url_hash in (md5digest(uri) for uri in self.software_release_filter_list)): url_hash in (md5digest(uri) for uri in self.software_release_filter_list)):
try: try:
software_release.building() if not self.api_backward_compatibility:
self.slap.jio_api_connector.put({
"portal_type": "Software Installation",
"compute_node_id": self.computer_id,
"software_release_uri": software_release_uri,
"reported_state": "building",
})
else:
software_release["compatibility_software_release"].building()
except NotFoundError: except NotFoundError:
pass pass
software.install() software.install()
...@@ -640,14 +713,32 @@ stderr_logfile_backups=1 ...@@ -640,14 +713,32 @@ stderr_logfile_backups=1
manager.softwareTearDown(software) manager.softwareTearDown(software)
# Send log before exiting # Send log before exiting
except (SystemExit, KeyboardInterrupt): except (SystemExit, KeyboardInterrupt):
software_release.error(traceback.format_exc(), logger=self.logger) if not self.api_backward_compatibility:
self.slap.jio_api_connector.put({
"portal_type": "Software Installation",
"compute_node_id": self.computer_id,
"software_release_uri": software_release_uri,
"error_status": traceback.format_exc(),
})
else:
software_release["compatibility_software_release"].error(
traceback.format_exc(), logger=self.logger
)
raise raise
# Buildout failed: send log but don't print it to output (already done) # Buildout failed: send log but don't print it to output (already done)
except BuildoutFailedError as exc: except BuildoutFailedError as exc:
clean_run = False clean_run = False
try: try:
software_release.error(exc, logger=self.logger) if not self.api_backward_compatibility:
self.slap.jio_api_connector.put({
"portal_type": "Software Installation",
"compute_node_id": self.computer_id,
"software_release_uri": software_release_uri,
"error_status": str(exc),
})
else:
software_release["compatibility_software_release"].error(exc, logger=self.logger)
except (SystemExit, KeyboardInterrupt): except (SystemExit, KeyboardInterrupt):
raise raise
except Exception: except Exception:
...@@ -656,17 +747,43 @@ stderr_logfile_backups=1 ...@@ -656,17 +747,43 @@ stderr_logfile_backups=1
# For everything else: log it, send it, continue. # For everything else: log it, send it, continue.
except Exception: except Exception:
self.logger.exception('') self.logger.exception('')
software_release.error(traceback.format_exc(), logger=self.logger) if not self.api_backward_compatibility:
self.slap.jio_api_connector.put({
"portal_type": "Software Installation",
"compute_node_id": self.computer_id,
"software_release_uri": software_release_uri,
"error_status": traceback.format_exc(),
})
else:
software_release["compatibility_software_release"].error(
traceback.format_exc(), logger=self.logger
)
clean_run = False clean_run = False
else: else:
if state == 'available': if state == 'available':
try: try:
software_release.available() if not self.api_backward_compatibility:
self.slap.jio_api_connector.put({
"portal_type": "Software Installation",
"compute_node_id": self.computer_id,
"software_release_uri": software_release_uri,
"reported_state": "available",
})
else:
software_release["compatibility_software_release"].available()
except (NotFoundError, ServerError): except (NotFoundError, ServerError):
pass pass
elif state == 'destroyed': elif state == 'destroyed':
try: try:
software_release.destroyed() if not self.api_backward_compatibility:
self.slap.jio_api_connector.put({
"portal_type": "Software Installation",
"compute_node_id": self.computer_id,
"software_release_uri": software_release_uri,
"reported_state": "destroyed",
})
else:
software_release["compatibility_software_release"].destroyed()
except (NotFoundError, ServerError): except (NotFoundError, ServerError):
self.logger.exception('') self.logger.exception('')
self.logger.info('Finished software releases.') self.logger.info('Finished software releases.')
...@@ -764,7 +881,7 @@ stderr_logfile_backups=1 ...@@ -764,7 +881,7 @@ stderr_logfile_backups=1
return PromiseLauncher(config=promise_config, logger=self.logger).run() return PromiseLauncher(config=promise_config, logger=self.logger).run()
def _endInstallationTransaction(self, computer_partition): def _endInstallationTransaction(self, computer_partition):
partition_id = computer_partition.getId() partition_id = computer_partition.get("compute_partition_id")
transaction_file_name = COMPUTER_PARTITION_REQUEST_LIST_TEMPLATE_FILENAME % partition_id transaction_file_name = COMPUTER_PARTITION_REQUEST_LIST_TEMPLATE_FILENAME % partition_id
transaction_file_path = os.path.join(self.instance_root, transaction_file_path = os.path.join(self.instance_root,
partition_id, partition_id,
...@@ -773,9 +890,16 @@ stderr_logfile_backups=1 ...@@ -773,9 +890,16 @@ stderr_logfile_backups=1
if os.path.exists(transaction_file_path): if os.path.exists(transaction_file_path):
with open(transaction_file_path, 'r') as tf: with open(transaction_file_path, 'r') as tf:
try: try:
computer_partition.setComputerPartitionRelatedInstanceList( if not self.api_backward_compatibility:
[reference for reference in tf.read().split('\n') if reference] self.slap.jio_api_connector.put({
) "portal_type": "Software Instance",
"reference": computer_partition.get("reference"),
"requested_instance_list": [reference for reference in tf.read().split('\n') if reference],
})
else:
computer_partition["slap_partition"].setComputerPartitionRelatedInstanceList(
[reference for reference in tf.read().split('\n') if reference]
)
except NotFoundError as e: except NotFoundError as e:
# Master doesn't implement this feature ? # Master doesn't implement this feature ?
self.logger.warning("NotFoundError: %s. \nCannot send requested instance "\ self.logger.warning("NotFoundError: %s. \nCannot send requested instance "\
...@@ -986,7 +1110,23 @@ stderr_logfile_backups=1 ...@@ -986,7 +1110,23 @@ stderr_logfile_backups=1
elif valid_ipv6(ip): elif valid_ipv6(ip):
ipv6_list.append(ip) ipv6_list.append(ip)
hosting_ip_list = computer_partition.getFullHostingIpAddressList() if not self.api_backward_compatibility:
hosting_ip_list = []
# Get all the instances of the instance tree
related_instance_list = self.slap.jio_api_connector.allDocs({
"portal_type": "Software Instance",
"root_instance_title": computer_partition["root_instance_title"],
}).get("result_list", [])
for instance_result in related_instance_list:
if instance_result["reference"] != computer_partition["reference"]:
instance = self.slap.jio_api_connector.get({
"portal_type": "Software Instance",
"reference": instance_result["reference"],
})
hosting_ip_list = hosting_ip_list + instance["ip_list"]
else:
hosting_ip_list = computer_partition["slap_partition"].getFullHostingIpAddressList()
for iface, ip in hosting_ip_list: for iface, ip in hosting_ip_list:
if valid_ipv4(ip): if valid_ipv4(ip):
if not ip in ipv4_list: if not ip in ipv4_list:
...@@ -995,7 +1135,7 @@ stderr_logfile_backups=1 ...@@ -995,7 +1135,7 @@ stderr_logfile_backups=1
if not ip in ipv6_list: if not ip in ipv6_list:
hosting_ipv6_list.append(ip) hosting_ipv6_list.append(ip)
filter_dict = getattr(computer_partition, '_filter_dict', None) filter_dict = computer_partition.get('sla_parameters', None)
extra_list = [] extra_list = []
accept_ip_list = [] accept_ip_list = []
if filter_dict is not None: if filter_dict is not None:
...@@ -1015,11 +1155,11 @@ stderr_logfile_backups=1 ...@@ -1015,11 +1155,11 @@ stderr_logfile_backups=1
for ip in ipv4_list: for ip in ipv4_list:
cmd_list = getFirewallRules(ip, hosting_ipv4_list, cmd_list = getFirewallRules(ip, hosting_ipv4_list,
source_ipv4_list, ip_type='ipv4') source_ipv4_list, ip_type='ipv4')
self._checkAddFirewallRules(computer_partition.getId(), self._checkAddFirewallRules(computer_partition.get("compute_partition_id"),
cmd_list, add=add_rules) cmd_list, add=add_rules)
def _checkPromiseAnomaly(self, local_partition, computer_partition): def _checkPromiseAnomaly(self, local_partition, computer_partition):
partition_access_status = computer_partition.getAccessStatus() partition_access_status = computer_partition.get("access_status_message", "")
status_error = False status_error = False
if partition_access_status and partition_access_status.startswith("#error"): if partition_access_status and partition_access_status.startswith("#error"):
status_error = True status_error = True
...@@ -1031,17 +1171,24 @@ stderr_logfile_backups=1 ...@@ -1031,17 +1171,24 @@ stderr_logfile_backups=1
self.logger.error(e) self.logger.error(e)
if partition_access_status is None or not status_error: if partition_access_status is None or not status_error:
local_partition._updateCertificate() local_partition._updateCertificate()
computer_partition.error(e, logger=self.logger) self.sendPartitionError(computer_partition, e, logger=self.logger)
else: else:
if partition_access_status is None or status_error: if partition_access_status is None or status_error:
local_partition._updateCertificate() local_partition._updateCertificate()
computer_partition.started() if not self.api_backward_compatibility:
self.slap.jio_api_connector.put({
"portal_type": "Software Instance",
"reference": computer_partition.get("reference"),
"reported_state": "started"
})
else:
computer_partition["slap_partition"].started()
def processPromise(self, computer_partition): def processPromise(self, computer_partition):
""" """
Process the promises from a given Computer Partition, depending on its state Process the promises from a given Computer Partition, depending on its state
""" """
computer_partition_id = computer_partition.getId() computer_partition_id = computer_partition.get("compute_partition_id")
# Sanity checks before processing # Sanity checks before processing
# Those values should not be None or empty string or any falsy value # Those values should not be None or empty string or any falsy value
...@@ -1050,12 +1197,7 @@ stderr_logfile_backups=1 ...@@ -1050,12 +1197,7 @@ stderr_logfile_backups=1
instance_path = os.path.join(self.instance_root, computer_partition_id) instance_path = os.path.join(self.instance_root, computer_partition_id)
os.environ['SLAPGRID_INSTANCE_ROOT'] = self.instance_root os.environ['SLAPGRID_INSTANCE_ROOT'] = self.instance_root
try: software_url = computer_partition.get("software_release_uri")
software_url = computer_partition.getSoftwareRelease().getURI()
except NotFoundError:
# Problem with instance: SR URI not set.
# Try to process it anyway, it may need to be deleted.
software_url = None
try: try:
software_path = os.path.join(self.software_root, md5digest(software_url)) software_path = os.path.join(self.software_root, md5digest(software_url))
...@@ -1064,7 +1206,7 @@ stderr_logfile_backups=1 ...@@ -1064,7 +1206,7 @@ stderr_logfile_backups=1
# Try to process it anyway, it may need to be deleted. # Try to process it anyway, it may need to be deleted.
software_path = None software_path = None
computer_partition_state = computer_partition.getState() computer_partition_state = computer_partition.get("state")
local_partition = Partition( local_partition = Partition(
software_path=software_path, software_path=software_path,
...@@ -1082,10 +1224,11 @@ stderr_logfile_backups=1 ...@@ -1082,10 +1224,11 @@ stderr_logfile_backups=1
buildout=self.buildout, buildout=self.buildout,
buildout_debug=self.buildout_debug, buildout_debug=self.buildout_debug,
logger=self.logger, logger=self.logger,
retention_delay=getattr(computer_partition, '_filter_dict', {}).get('retention_delay', '0'), retention_delay=computer_partition.get('sla_parameters', {}).get('retention_delay', '0'),
instance_min_free_space=self.instance_min_free_space, instance_min_free_space=self.instance_min_free_space,
instance_storage_home=self.instance_storage_home, instance_storage_home=self.instance_storage_home,
ipv4_global_network=self.ipv4_global_network, ipv4_global_network=self.ipv4_global_network,
api_backward_compatibility=self.api_backward_compatibility,
) )
self.logger.info('Processing Promises for Computer Partition %s.', computer_partition_id) self.logger.info('Processing Promises for Computer Partition %s.', computer_partition_id)
...@@ -1101,7 +1244,7 @@ stderr_logfile_backups=1 ...@@ -1101,7 +1244,7 @@ stderr_logfile_backups=1
""" """
Process a Computer Partition, depending on its state Process a Computer Partition, depending on its state
""" """
computer_partition_id = computer_partition.getId() computer_partition_id = computer_partition.get("compute_partition_id")
# Sanity checks before processing # Sanity checks before processing
# Those values should not be None or empty string or any falsy value # Those values should not be None or empty string or any falsy value
...@@ -1125,20 +1268,14 @@ stderr_logfile_backups=1 ...@@ -1125,20 +1268,14 @@ stderr_logfile_backups=1
instance_path, instance_path,
COMPUTER_PARTITION_TIMESTAMP_FILENAME COMPUTER_PARTITION_TIMESTAMP_FILENAME
) )
parameter_dict = computer_partition.getInstanceParameterDict() timestamp = computer_partition.get("processing_timestamp")
timestamp = parameter_dict.get('timestamp')
error_output_file = os.path.join( error_output_file = os.path.join(
instance_path, instance_path,
COMPUTER_PARTITION_INSTALL_ERROR_FILENAME % computer_partition_id COMPUTER_PARTITION_INSTALL_ERROR_FILENAME % computer_partition_id
) )
try: software_url = computer_partition.get("software_release_uri")
software_url = computer_partition.getSoftwareRelease().getURI()
except NotFoundError:
# Problem with instance: SR URI not set.
# Try to process it anyway, it may need to be deleted.
software_url = None
try: try:
software_path = os.path.join(self.software_root, md5digest(software_url)) software_path = os.path.join(self.software_root, md5digest(software_url))
except TypeError: except TypeError:
...@@ -1146,7 +1283,7 @@ stderr_logfile_backups=1 ...@@ -1146,7 +1283,7 @@ stderr_logfile_backups=1
# Try to process it anyway, it may need to be deleted. # Try to process it anyway, it may need to be deleted.
software_path = None software_path = None
computer_partition_state = computer_partition.getState() computer_partition_state = computer_partition.get("state")
periodicity = self.maximum_periodicity periodicity = self.maximum_periodicity
if software_path: if software_path:
periodicity_path = os.path.join(software_path, 'periodicity') periodicity_path = os.path.join(software_path, 'periodicity')
...@@ -1174,7 +1311,7 @@ stderr_logfile_backups=1 ...@@ -1174,7 +1311,7 @@ stderr_logfile_backups=1
buildout=self.buildout, buildout=self.buildout,
buildout_debug=self.buildout_debug, buildout_debug=self.buildout_debug,
logger=self.logger, logger=self.logger,
retention_delay=getattr(computer_partition, '_filter_dict', {}).get('retention_delay', '0'), retention_delay=computer_partition.get('sla_parameters', {}).get('retention_delay', '0'),
instance_min_free_space=self.instance_min_free_space, instance_min_free_space=self.instance_min_free_space,
instance_storage_home=self.instance_storage_home, instance_storage_home=self.instance_storage_home,
ipv4_global_network=self.ipv4_global_network, ipv4_global_network=self.ipv4_global_network,
...@@ -1254,7 +1391,7 @@ stderr_logfile_backups=1 ...@@ -1254,7 +1391,7 @@ stderr_logfile_backups=1
local_partition._updateCertificate() local_partition._updateCertificate()
# XXX this line breaks 37 tests # XXX this line breaks 37 tests
# self.logger.info(' Instance type: %s' % computer_partition.getType()) # self.logger.info(' Instance type: %s' % computer_partition.get("software_type"))
self.logger.info(' Instance status: %s' % computer_partition_state) self.logger.info(' Instance status: %s' % computer_partition_state)
if os.path.exists(error_output_file): if os.path.exists(error_output_file):
...@@ -1262,7 +1399,7 @@ stderr_logfile_backups=1 ...@@ -1262,7 +1399,7 @@ stderr_logfile_backups=1
partition_ip_list = full_hosting_ip_list = [] partition_ip_list = full_hosting_ip_list = []
if self.firewall_conf: if self.firewall_conf:
partition_ip_list = parameter_dict['ip_list'] + parameter_dict.get( partition_ip_list = computer_partition['ip_list'] + computer_partition.get(
'full_ip_list', []) 'full_ip_list', [])
if computer_partition_state == COMPUTER_PARTITION_STARTED_STATE: if computer_partition_state == COMPUTER_PARTITION_STARTED_STATE:
...@@ -1276,7 +1413,14 @@ stderr_logfile_backups=1 ...@@ -1276,7 +1413,14 @@ stderr_logfile_backups=1
partition_ip_list) partition_ip_list)
if not self.force_stop: if not self.force_stop:
self._checkPromiseList(local_partition) self._checkPromiseList(local_partition)
computer_partition.started() if not self.api_backward_compatibility:
self.slap.jio_api_connector.put({
"portal_type": "Software Instance",
"reference": computer_partition.get("reference"),
"reported_state": "started"
})
else:
computer_partition["slap_partition"].started()
self._endInstallationTransaction(computer_partition) self._endInstallationTransaction(computer_partition)
elif computer_partition_state == COMPUTER_PARTITION_STOPPED_STATE: elif computer_partition_state == COMPUTER_PARTITION_STOPPED_STATE:
try: try:
...@@ -1290,9 +1434,16 @@ stderr_logfile_backups=1 ...@@ -1290,9 +1434,16 @@ stderr_logfile_backups=1
# Instance has to be stopped even if buildout/reporting is wrong. # Instance has to be stopped even if buildout/reporting is wrong.
local_partition.stop() local_partition.stop()
try: try:
computer_partition.stopped() if not self.api_backward_compatibility:
self.slap.jio_api_connector.put({
"portal_type": "Software Instance",
"reference": computer_partition.get("reference"),
"reported_state": "stopped"
})
else:
computer_partition["slap_partition"].stopped()
except (SystemExit, KeyboardInterrupt): except (SystemExit, KeyboardInterrupt):
computer_partition.error(traceback.format_exc(), logger=self.logger) self.sendPartitionError(computer_partition, traceback.format_exc(), logger=self.logger)
raise raise
except Exception: except Exception:
pass pass
...@@ -1304,16 +1455,23 @@ stderr_logfile_backups=1 ...@@ -1304,16 +1455,23 @@ stderr_logfile_backups=1
partition_ip_list, partition_ip_list,
drop_entries=True) drop_entries=True)
try: try:
computer_partition.stopped() if not self.api_backward_compatibility:
self.slap.jio_api_connector.put({
"portal_type": "Software Instance",
"reference": computer_partition.get("reference"),
"reported_state": "stopped"
})
else:
computer_partition["slap_partition"].stopped()
except (SystemExit, KeyboardInterrupt): except (SystemExit, KeyboardInterrupt):
computer_partition.error(traceback.format_exc(), logger=self.logger) self.sendPartitionError(computer_partition, traceback.format_exc(), logger=self.logger)
raise raise
except Exception: except Exception:
pass pass
else: else:
error_string = "Computer Partition %r has unsupported state: %s" % \ error_string = "Computer Partition %r has unsupported state: %s" % \
(computer_partition_id, computer_partition_state) (computer_partition_id, computer_partition_state)
computer_partition.error(error_string, logger=self.logger) self.sendPartitionError(computer_partition, error_string, logger=self.logger)
raise NotImplementedError(error_string) raise NotImplementedError(error_string)
except Exception as e: except Exception as e:
if not isinstance(e, PromiseError): if not isinstance(e, PromiseError):
...@@ -1349,7 +1507,7 @@ stderr_logfile_backups=1 ...@@ -1349,7 +1507,7 @@ stderr_logfile_backups=1
for computer_partition in computer_partition_list: for computer_partition in computer_partition_list:
try: try:
computer_partition_path = os.path.join(self.instance_root, computer_partition_path = os.path.join(self.instance_root,
computer_partition.getId()) computer_partition.get("compute_partition_id"))
if not os.path.exists(computer_partition_path): if not os.path.exists(computer_partition_path):
raise NotFoundError('Partition directory %s does not exist.' % raise NotFoundError('Partition directory %s does not exist.' %
computer_partition_path) computer_partition_path)
...@@ -1358,11 +1516,8 @@ stderr_logfile_backups=1 ...@@ -1358,11 +1516,8 @@ stderr_logfile_backups=1
# partition, and check if it has some Software information. # partition, and check if it has some Software information.
# XXX-Cedric: Temporary AND ugly solution to check if an instance # XXX-Cedric: Temporary AND ugly solution to check if an instance
# is in the partition. Dangerous because not 100% sure it is empty # is in the partition. Dangerous because not 100% sure it is empty
computer_partition_state = computer_partition.getState() computer_partition_state = computer_partition.get("state")
try: software_url = computer_partition.get("software_release_uri")
software_url = computer_partition.getSoftwareRelease().getURI()
except (NotFoundError, TypeError, NameError):
software_url = None
if computer_partition_state == COMPUTER_PARTITION_DESTROYED_STATE and \ if computer_partition_state == COMPUTER_PARTITION_DESTROYED_STATE and \
not software_url: not software_url:
# Exclude files which may come from concurrent processing # Exclude files which may come from concurrent processing
...@@ -1380,7 +1535,7 @@ stderr_logfile_backups=1 ...@@ -1380,7 +1535,7 @@ stderr_logfile_backups=1
# Ignore .slapos-resource file dumped by slapformat. # Ignore .slapos-resource file dumped by slapformat.
if os.listdir(computer_partition_path) not in empty_partition_listdir: if os.listdir(computer_partition_path) not in empty_partition_listdir:
self.logger.warning("Free partition %s contains file(s) in %s." % ( self.logger.warning("Free partition %s contains file(s) in %s." % (
computer_partition.getId(), computer_partition_path)) computer_partition.get("compute_partition_id"), computer_partition_path))
continue continue
# Everything seems fine # Everything seems fine
...@@ -1390,7 +1545,7 @@ stderr_logfile_backups=1 ...@@ -1390,7 +1545,7 @@ stderr_logfile_backups=1
# Send log before exiting # Send log before exiting
except (SystemExit, KeyboardInterrupt): except (SystemExit, KeyboardInterrupt):
computer_partition.error(traceback.format_exc(), logger=self.logger) self.sendPartitionError(computer_partition, traceback.format_exc(), logger=self.logger)
raise raise
except Exception as exc: except Exception as exc:
...@@ -1399,7 +1554,7 @@ stderr_logfile_backups=1 ...@@ -1399,7 +1554,7 @@ stderr_logfile_backups=1
# For everything else: log it, send it, continue. # For everything else: log it, send it, continue.
self.logger.exception('') self.logger.exception('')
try: try:
computer_partition.error(exc, logger=self.logger) self.sendPartitionError(computer_partition, exc, logger=self.logger)
except (SystemExit, KeyboardInterrupt): except (SystemExit, KeyboardInterrupt):
raise raise
except Exception: except Exception:
...@@ -1430,20 +1585,25 @@ stderr_logfile_backups=1 ...@@ -1430,20 +1585,25 @@ stderr_logfile_backups=1
# Nothing should raise outside of the current loop iteration, so that # Nothing should raise outside of the current loop iteration, so that
# even if something is terribly wrong while processing an instance, it # even if something is terribly wrong while processing an instance, it
# won't prevent processing other ones. # won't prevent processing other ones.
if not self.api_backward_compatibility:
computer_partition = self.slap.jio_api_connector.get({
"portal_type": "Software Instance",
"reference": computer_partition["reference"]
})
try: try:
# Process the partition itself # Process the partition itself
self.processComputerPartition(computer_partition) self.processComputerPartition(computer_partition)
# Send log before exiting # Send log before exiting
except (SystemExit, KeyboardInterrupt): except (SystemExit, KeyboardInterrupt):
computer_partition.error(traceback.format_exc(), logger=self.logger) self.sendPartitionError(computer_partition, traceback.format_exc(), logger=self.logger)
raise raise
except PromiseError as exc: except PromiseError as exc:
clean_run_promise = False clean_run_promise = False
try: try:
self.logger.error(exc) self.logger.error(exc)
computer_partition.error(exc, logger=self.logger) self.sendPartitionError(computer_partition, exc, logger=self.logger)
promise_error_partition_list.append((computer_partition, exc)) promise_error_partition_list.append((computer_partition, exc))
except (SystemExit, KeyboardInterrupt): except (SystemExit, KeyboardInterrupt):
raise raise
...@@ -1457,7 +1617,7 @@ stderr_logfile_backups=1 ...@@ -1457,7 +1617,7 @@ stderr_logfile_backups=1
# For everything else: log it, send it, continue. # For everything else: log it, send it, continue.
self.logger.exception('') self.logger.exception('')
try: try:
computer_partition.error(exc, logger=self.logger) self.sendPartitionError(computer_partition, exc, logger=self.logger)
process_error_partition_list.append((computer_partition, exc)) process_error_partition_list.append((computer_partition, exc))
except (SystemExit, KeyboardInterrupt): except (SystemExit, KeyboardInterrupt):
raise raise
...@@ -1467,9 +1627,8 @@ stderr_logfile_backups=1 ...@@ -1467,9 +1627,8 @@ stderr_logfile_backups=1
def getPartitionType(part): def getPartitionType(part):
"""returns the partition type, if known at that point. """returns the partition type, if known at that point.
""" """
try: software_type = partition.get("software_type", None)
return part.getType() if software_type is None:
except slapos.slap.ResourceNotReady:
return '(not ready)' return '(not ready)'
self.logger.info('Finished computer partitions.') self.logger.info('Finished computer partitions.')
...@@ -1477,11 +1636,11 @@ stderr_logfile_backups=1 ...@@ -1477,11 +1636,11 @@ stderr_logfile_backups=1
if process_error_partition_list: if process_error_partition_list:
self.logger.info('Error while processing the following partitions:') self.logger.info('Error while processing the following partitions:')
for partition, exc in process_error_partition_list: for partition, exc in process_error_partition_list:
self.logger.info(' %s[%s]: %s', partition.getId(), getPartitionType(partition), exc) self.logger.info(' %s[%s]: %s', partition.get("compute_partition_id"), getPartitionType(partition), exc)
if promise_error_partition_list: if promise_error_partition_list:
self.logger.info('Error with promises for the following partitions:') self.logger.info('Error with promises for the following partitions:')
for partition, exc in promise_error_partition_list: for partition, exc in promise_error_partition_list:
self.logger.info(' %s[%s]: %s', partition.getId(), getPartitionType(partition), exc) self.logger.info(' %s[%s]: %s', partition.get("compute_partition_id"), getPartitionType(partition), exc)
# Return success value # Return success value
if not clean_run: if not clean_run:
...@@ -1502,6 +1661,11 @@ stderr_logfile_backups=1 ...@@ -1502,6 +1661,11 @@ stderr_logfile_backups=1
promise_error_partition_list = [] promise_error_partition_list = []
for computer_partition in computer_partition_list: for computer_partition in computer_partition_list:
if not self.api_backward_compatibility:
computer_partition = self.slap.jio_api_connector.get({
"portal_type": "Software Instance",
"reference": computer_partition["reference"]
})
try: try:
# Process the partition itself # Process the partition itself
self.processPromise(computer_partition) self.processPromise(computer_partition)
...@@ -1517,15 +1681,14 @@ stderr_logfile_backups=1 ...@@ -1517,15 +1681,14 @@ stderr_logfile_backups=1
def getPartitionType(part): def getPartitionType(part):
"""returns the partition type, if known at that point. """returns the partition type, if known at that point.
""" """
try: software_type = partition.get("software_type", None)
return part.getType() if software_type is None:
except slapos.slap.ResourceNotReady:
return '(not ready)' return '(not ready)'
if promise_error_partition_list: if promise_error_partition_list:
self.logger.info('Finished computer partitions.') self.logger.info('Finished computer partitions.')
for partition, exc in promise_error_partition_list: for partition, exc in promise_error_partition_list:
self.logger.info(' %s[%s]: %s', partition.getId(), getPartitionType(partition), exc) self.logger.info(' %s[%s]: %s', partition.get("compute_partition_id"), getPartitionType(partition), exc)
# Return success value # Return success value
if not clean_run_promise: if not clean_run_promise:
...@@ -1618,7 +1781,6 @@ stderr_logfile_backups=1 ...@@ -1618,7 +1781,6 @@ stderr_logfile_backups=1
self.checkEnvironmentAndCreateStructure() self.checkEnvironmentAndCreateStructure()
self._launchSupervisord() self._launchSupervisord()
slap_computer_usage = self.slap.registerComputer(self.computer_id)
computer_partition_usage_list = [] computer_partition_usage_list = []
self.logger.info('Aggregating and sending usage reports...') self.logger.info('Aggregating and sending usage reports...')
...@@ -1648,11 +1810,11 @@ stderr_logfile_backups=1 ...@@ -1648,11 +1810,11 @@ stderr_logfile_backups=1
clean_run = True clean_run = True
# Loop over the different computer partitions # Loop over the different computer partitions
computer_partition_list = self.FilterComputerPartitionList( computer_partition_list = self.FilterComputerPartitionList(
slap_computer_usage.getComputerPartitionList()) self.getComputerPartitionList())
for computer_partition in computer_partition_list: for computer_partition in computer_partition_list:
try: try:
computer_partition_id = computer_partition.getId() computer_partition_id = computer_partition.get("compute_partition_id")
instance_path = os.path.join(self.instance_root, computer_partition_id) instance_path = os.path.join(self.instance_root, computer_partition_id)
...@@ -1688,18 +1850,18 @@ stderr_logfile_backups=1 ...@@ -1688,18 +1850,18 @@ stderr_logfile_backups=1
failed_script_list.append("Script %r failed." % script) failed_script_list.append("Script %r failed." % script)
self.logger.warning('Failed to run %r' % invocation_list) self.logger.warning('Failed to run %r' % invocation_list)
if len(failed_script_list): if len(failed_script_list):
computer_partition.error('\n'.join(failed_script_list), logger=self.logger) self.sendPartitionError(computer_partition, '\n'.join(failed_script_list), logger=self.logger)
# Whatever happens, don't stop processing other instances # Whatever happens, don't stop processing other instances
except Exception: except Exception:
self.logger.exception('Cannot run usage script(s) for %r:' % self.logger.exception('Cannot run usage script(s) for %r:' %
computer_partition.getId()) computer_partition.get("compute_partition_id"))
# Now we loop through the different computer partitions to report # Now we loop through the different computer partitions to report
report_usage_issue_cp_list = [] report_usage_issue_cp_list = []
for computer_partition in computer_partition_list: for computer_partition in computer_partition_list:
try: try:
filename_delete_list = [] filename_delete_list = []
computer_partition_id = computer_partition.getId() computer_partition_id = computer_partition.get("compute_partition_id")
instance_path = os.path.join(self.instance_root, computer_partition_id) instance_path = os.path.join(self.instance_root, computer_partition_id)
dir_report_list = [os.path.join(instance_path, 'var', 'xml_report'), dir_report_list = [os.path.join(instance_path, 'var', 'xml_report'),
os.path.join(self.instance_root, 'var', 'xml_report', os.path.join(self.instance_root, 'var', 'xml_report',
...@@ -1744,7 +1906,7 @@ stderr_logfile_backups=1 ...@@ -1744,7 +1906,7 @@ stderr_logfile_backups=1
# Whatever happens, don't stop processing other instances # Whatever happens, don't stop processing other instances
except Exception: except Exception:
self.logger.exception('Cannot run usage script(s) for %r:' % self.logger.exception('Cannot run usage script(s) for %r:' %
computer_partition.getId()) computer_partition.get("compute_partition_id"))
for computer_partition_usage in computer_partition_usage_list: for computer_partition_usage in computer_partition_usage_list:
self.logger.info('computer_partition_usage_list: %s - %s' % self.logger.info('computer_partition_usage_list: %s - %s' %
...@@ -1770,7 +1932,7 @@ stderr_logfile_backups=1 ...@@ -1770,7 +1932,7 @@ stderr_logfile_backups=1
if self.validateXML(usage, computer_consumption_model): if self.validateXML(usage, computer_consumption_model):
self.logger.info('XML file generated by asXML is valid') self.logger.info('XML file generated by asXML is valid')
slap_computer_usage.reportUsage(usage) self.computer.reportUsage(usage)
filename_delete_list.append(filename) filename_delete_list.append(filename)
else: else:
self.logger.info('XML file is invalid %s' % file_path) self.logger.info('XML file is invalid %s' % file_path)
...@@ -1790,34 +1952,35 @@ stderr_logfile_backups=1 ...@@ -1790,34 +1952,35 @@ stderr_logfile_backups=1
# We test the XML report before sending it # We test the XML report before sending it
if self.validateXML(computer_consumption, computer_consumption_model): if self.validateXML(computer_consumption, computer_consumption_model):
self.logger.info('XML file generated by asXML is valid') self.logger.info('XML file generated by asXML is valid')
slap_computer_usage.reportUsage(computer_consumption) self.computer.reportUsage(computer_consumption)
else: else:
self.logger.info('XML file generated by asXML is not valid !') self.logger.info('XML file generated by asXML is not valid !')
raise ValueError('XML file generated by asXML is not valid !') raise ValueError('XML file generated by asXML is not valid !')
except Exception: except Exception:
issue = "Cannot report usage for %r: %s" % ( issue = "Cannot report usage for %r: %s" % (
computer_partition.getId(), computer_partition.get("compute_partition_id"),
traceback.format_exc()) traceback.format_exc())
self.logger.info(issue) self.logger.info(issue)
computer_partition.error(issue, logger=self.logger) self.sendPartitionError(computer_partition, issue, logger=self.logger)
report_usage_issue_cp_list.append(computer_partition_id) report_usage_issue_cp_list.append(computer_partition_id)
for computer_partition in computer_partition_list: for computer_partition in computer_partition_list:
if computer_partition.getState() == COMPUTER_PARTITION_DESTROYED_STATE: if computer_partition.get("state") == COMPUTER_PARTITION_DESTROYED_STATE:
destroyed = False destroyed = False
try: try:
computer_partition_id = computer_partition.getId() computer_partition_id = computer_partition.get("compute_partition_id")
software_url = computer_partition.get("software_release_uri")
try: try:
software_url = computer_partition.getSoftwareRelease().getURI()
software_path = os.path.join(self.software_root, md5digest(software_url)) software_path = os.path.join(self.software_root, md5digest(software_url))
except (NotFoundError, TypeError): except TypeError:
software_url = None # Problem with instance: SR URI not set.
# Try to process it anyway, it may need to be deleted.
software_path = None software_path = None
local_partition = Partition( local_partition = Partition(
software_path=software_path, software_path=software_path,
instance_path=os.path.join(self.instance_root, instance_path=os.path.join(self.instance_root,
computer_partition.getId()), computer_partition.get("compute_partition_id")),
supervisord_partition_configuration_path=os.path.join( supervisord_partition_configuration_path=os.path.join(
_getSupervisordConfigurationDirectory(self.instance_root), '%s.conf' % _getSupervisordConfigurationDirectory(self.instance_root), '%s.conf' %
computer_partition_id), computer_partition_id),
...@@ -1837,9 +2000,16 @@ stderr_logfile_backups=1 ...@@ -1837,9 +2000,16 @@ stderr_logfile_backups=1
local_partition.stop() local_partition.stop()
local_partition._updateCertificate() local_partition._updateCertificate()
try: try:
computer_partition.stopped() if not self.api_backward_compatibility:
self.slap.jio_api_connector.put({
"portal_type": "Software Instance",
"reference": computer_partition.get("reference"),
"reported_state": "stopped"
})
else:
computer_partition["slap_partition"].stopped()
except (SystemExit, KeyboardInterrupt): except (SystemExit, KeyboardInterrupt):
computer_partition.error(traceback.format_exc(), logger=self.logger) self.sendPartitionError(computer_partition, traceback.format_exc(), logger=self.logger)
raise raise
except Exception: except Exception:
pass pass
...@@ -1847,9 +2017,9 @@ stderr_logfile_backups=1 ...@@ -1847,9 +2017,9 @@ stderr_logfile_backups=1
for manager in self._manager_list: for manager in self._manager_list:
manager.report(local_partition) manager.report(local_partition)
if computer_partition.getId() in report_usage_issue_cp_list: if computer_partition.get("compute_partition_id") in report_usage_issue_cp_list:
self.logger.info('Ignoring destruction of %r, as no report usage was sent' % self.logger.info('Ignoring destruction of %r, as no report usage was sent' %
computer_partition.getId()) computer_partition.get("compute_partition_id"))
continue continue
if self._checkWaitProcessList(local_partition, if self._checkWaitProcessList(local_partition,
state_list=['RUNNING', 'STARTING']): state_list=['RUNNING', 'STARTING']):
...@@ -1858,24 +2028,31 @@ stderr_logfile_backups=1 ...@@ -1858,24 +2028,31 @@ stderr_logfile_backups=1
continue continue
destroyed = local_partition.destroy() destroyed = local_partition.destroy()
except (SystemExit, KeyboardInterrupt): except (SystemExit, KeyboardInterrupt):
computer_partition.error(traceback.format_exc(), logger=self.logger) self.sendPartitionError(computer_partition, traceback.format_exc(), logger=self.logger)
raise raise
except Exception: except Exception:
clean_run = False clean_run = False
self.logger.exception('') self.logger.exception('')
exc = traceback.format_exc() exc = traceback.format_exc()
computer_partition.error(exc, logger=self.logger) self.sendPartitionError(computer_partition, exc, logger=self.logger)
try: try:
if destroyed: if destroyed:
computer_partition.destroyed() if not self.api_backward_compatibility:
self.slap.jio_api_connector.put({
"portal_type": "Software Instance",
"reference": computer_partition.get("reference"),
"reported_state": "destroyed"
})
else:
computer_partition["slap_partition"].destroyed()
except NotFoundError: except NotFoundError:
self.logger.debug('Ignored slap error while trying to inform about ' self.logger.debug('Ignored slap error while trying to inform about '
'destroying not fully configured Computer Partition %r' % 'destroying not fully configured Computer Partition %r' %
computer_partition.getId()) computer_partition.get("compute_partition_id"))
except ServerError as server_error: except ServerError as server_error:
self.logger.debug('Ignored server error while trying to inform about ' self.logger.debug('Ignored server error while trying to inform about '
'destroying Computer Partition %r. Error is:\n%r' % 'destroying Computer Partition %r. Error is:\n%r' %
(computer_partition.getId(), server_error.args[0])) (computer_partition.get("compute_partition_id"), server_error.args[0]))
self.logger.info('Finished usage reports.') self.logger.info('Finished usage reports.')
......
...@@ -82,9 +82,8 @@ class Manager(object): ...@@ -82,9 +82,8 @@ class Manager(object):
# Get partitions IPv6 address # Get partitions IPv6 address
computer_partition = partition.computer_partition computer_partition = partition.computer_partition
parameter_dict = computer_partition.getInstanceParameterDict()
partition_ip_list = parameter_dict['ip_list'] + parameter_dict.get( partition_ip_list = computer_partition['ip_list'] + computer_partition.get(
'full_ip_list', []) 'full_ip_list', [])
partition_ip_list = [tup[1] for tup in partition_ip_list] partition_ip_list = [tup[1] for tup in partition_ip_list]
......
...@@ -741,6 +741,36 @@ class SlapConnectionHelper(ConnectionHelper): ...@@ -741,6 +741,36 @@ class SlapConnectionHelper(ConnectionHelper):
return loads(xml) return loads(xml)
# https://stackoverflow.com/a/33571117
def _byteify(data, ignore_dicts = False):
if isinstance(data, str):
return data
# if this is a list of values, return list of byteified values
if isinstance(data, list):
return [ _byteify(item, ignore_dicts=True) for item in data ]
# if this is a dictionary, return dictionary of byteified keys and values
# but only if we haven't already byteified it
if isinstance(data, dict) and not ignore_dicts:
return {
_byteify(key, ignore_dicts=True): _byteify(value, ignore_dicts=True)
for key, value in data.items() # changed to .items() for python 2.7/3
}
# python 3 compatible duck-typing
# if this is a unicode string, return its string representation
if str(type(data)) == "<type 'unicode'>":
return data.encode('utf-8')
# if it's anything else, return it in its original form
return data
def json_loads_byteified(json_text):
return _byteify(
json.loads(json_text, object_hook=_byteify),
ignore_dicts=True
)
class JioAPIConnectionHelper(ConnectionHelper): class JioAPIConnectionHelper(ConnectionHelper):
def apiCall(self, path, data): def apiCall(self, path, data):
...@@ -749,7 +779,7 @@ class JioAPIConnectionHelper(ConnectionHelper): ...@@ -749,7 +779,7 @@ class JioAPIConnectionHelper(ConnectionHelper):
data=json.dumps(data), data=json.dumps(data),
headers={'Content-type': 'application/json'}, headers={'Content-type': 'application/json'},
expect_json_error=True) expect_json_error=True)
return req.json() return json_loads_byteified(req.text)
def get(self, data): def get(self, data):
return self.apiCall(path="get/", return self.apiCall(path="get/",
......
...@@ -193,7 +193,10 @@ class BasicMixin(object): ...@@ -193,7 +193,10 @@ class BasicMixin(object):
logger=logging.getLogger(), logger=logging.getLogger(),
shared_part_list=self.shared_parts_root, shared_part_list=self.shared_parts_root,
force_stop=force_stop, force_stop=force_stop,
certificate_repository_path=self.certificate_repository_path) certificate_repository_path=self.certificate_repository_path,
slapgrid_jio_uri=self.master_url + "api/",
)
self.use_jio_api = True
self.grid._manager_list = self.manager_list self.grid._manager_list = self.manager_list
# monkey patch buildout bootstrap # monkey patch buildout bootstrap
...@@ -378,6 +381,29 @@ class MasterMixin(BasicMixin): ...@@ -378,6 +381,29 @@ class MasterMixin(BasicMixin):
self._unmock_sleep() self._unmock_sleep()
BasicMixin.tearDown(self) BasicMixin.tearDown(self)
class SlapToolMasterMixin(MasterMixin):
def setSlapgrid(self, develop=False, force_stop=False):
if getattr(self, 'master_url', None) is None:
self.master_url = 'http://127.0.0.1:80/'
self.computer_id = 'computer'
self.supervisord_socket = os.path.join(self._tempdir, 'sv.sock')
self.supervisord_configuration_path = os.path.join(self._tempdir,
'supervisord')
self.usage_report_periodicity = 1
self.buildout = None
self.grid = slapgrid.Slapgrid(self.software_root,
self.instance_root,
self.master_url,
self.computer_id,
self.buildout,
develop=develop,
logger=logging.getLogger(),
shared_part_list=self.shared_parts_root,
force_stop=force_stop,
)
self.grid._manager_list = self.manager_list
self.use_jio_api = False
# monkey patch buildout bootstrap
class ComputerForTest(object): class ComputerForTest(object):
""" """
...@@ -393,6 +419,7 @@ class ComputerForTest(object): ...@@ -393,6 +419,7 @@ class ComputerForTest(object):
Will set up instances, software and sequence Will set up instances, software and sequence
""" """
self.sequence = [] self.sequence = []
self.body_sequence = []
self.instance_amount = instance_amount self.instance_amount = instance_amount
self.software_amount = software_amount self.software_amount = software_amount
self.software_root = software_root self.software_root = software_root
...@@ -421,6 +448,163 @@ class ComputerForTest(object): ...@@ -421,6 +448,163 @@ class ComputerForTest(object):
qs = parse.parse_qs(url.query) qs = parse.parse_qs(url.query)
else: else:
qs = parse.parse_qs(req.body) qs = parse.parse_qs(req.body)
# Catch API calls
if url.path.startswith('/api/'):
content = json.loads(req.body)
self.body_sequence.append(content)
if (url.path == '/api/allDocs/'):
if content["portal_type"] == "Software Installation":
return json.dumps({
"result_list": [{
"software_release_uri": x.name,
"portal_type": "Software Installation",
"compute_node_id": content["compute_node_id"],
"state": x.requested_state
} for x in self.software_list]
})
if content["portal_type"] == "Software Instance":
if "compute_node_id" in content:
return json.dumps({
"result_list": [{
"software_release_uri": x.software.name if x.software else None,
"reference": x.name,
"title": x.name,
"portal_type": "Software Instance",
"compute_partition_id": x.name,
"state": x.requested_state
} for x in self.instance_list]
})
elif "root_instance_title" in content:
return json.dumps({
"result_list": [{
"software_release_uri": x.software.name if x.software else None,
"reference": x.name,
"title": x.name,
"portal_type": "Software Instance",
"compute_partition_id": x.name,
"state": x.requested_state
} for x in self.instance_list] + [
{
"software_release_uri": "foo.cfg",
"reference": "related_instance",
"title": "related_instance",
"portal_type": "Software Instance",
"compute_partition_id": "related_instance",
"state": "stopped"
}
]
})
elif (url.path == '/api/put/'):
if content["portal_type"] == "Software Installation":
software = self.software_list[0]
software.sequence.append((url.path, content))
if "error_status" in content:
software.error_log = content['error_status']
software.error = True
return json.dumps({"id": content["software_release_uri"]})
elif content["portal_type"] == "Software Instance":
reference = content["reference"]
requested_instance = None
for instance in self.instance_list:
if instance.name == reference:
requested_instance = instance
break
if requested_instance:
requested_instance.sequence.append((url.path, content))
if "reported_state" in content:
if content["reported_state"] == "error":
instance.error = True
instance.error_log = content["status_message"]
else:
requested_instance.state = content["reported_state"]
return json.dumps({
"reference": requested_instance.name,
"portal_type": "Software Instance",
"success": "Done"
}, indent=2)
if "requested_instance_list" in content:
return json.dumps({
"reference": requested_instance.name,
"portal_type": "Software Instance",
"success": "Done"
}, indent=2)
else:
return json.dumps({
"status": "404",
"message": "No document found with parameters: %s" % reference,
"name": "NotFound",
})
elif (url.path == '/api/get/'):
if content["portal_type"] == "Software Instance":
reference = content["reference"]
# Treat the case of firewall
if reference == "related_instance":
return json.dumps({
"title": "related_instance",
"reference": "related_instance",
"software_release_uri": "foo.cfg",
"software_type": None,
"state": "stopped",
"connection_parameters": {
},
"parameters": {},
"shared": False,
"root_instance_title": "0",
"ip_list": self.ip_address_list,
"X509": {
"certificate": "",
"key": ""
},
"sla_parameters": {},
"compute_node_id": None,
"compute_partition_id": "requested_instance",
"processing_timestamp": 0,
"access_status_message": "",
"portal_type": "Software Instance"
})
requested_instance = None
for instance in self.instance_list:
if instance.name == reference:
requested_instance = instance
break
if requested_instance:
requested_instance.sequence.append((url.path, content))
return json.dumps({
"title": requested_instance.name,
"reference": requested_instance.name,
"software_release_uri": requested_instance.software.name,
"software_type": None,
"state": requested_instance.requested_state,
"connection_parameters": {
},
"parameters": {},
"shared": False,
"root_instance_title": requested_instance.name,
"ip_list": requested_instance.ip_list,
"full_ip_list": requested_instance.full_ip_list,
"X509": {
"certificate": requested_instance.certificate,
"key": requested_instance.key
},
"sla_parameters": requested_instance.filter_dict,
"compute_node_id": None,
"compute_partition_id": requested_instance.name,
"processing_timestamp": requested_instance.timestamp,
"access_status_message": requested_instance.error_log,
"portal_type": "Software Instance"
})
else:
return json.dumps({
"status": "404",
"message": "No document found with parameters: %s" % reference,
"name": "NotFound",
})
raise ValueError("Unexcepted call to API. URL:%s Content:%s" % (url.path, req.body))
if (url.path == '/getFullComputerInformation' if (url.path == '/getFullComputerInformation'
and 'computer_id' in qs): and 'computer_id' in qs):
slap_computer = self.getComputer(qs['computer_id'][0]) slap_computer = self.getComputer(qs['computer_id'][0])
...@@ -557,6 +741,10 @@ class InstanceForTest(object): ...@@ -557,6 +741,10 @@ class InstanceForTest(object):
self.ip_list = [('interface0', '10.0.8.2')] self.ip_list = [('interface0', '10.0.8.2')]
self.full_ip_list = [('route_interface0', '10.10.2.3', '10.10.0.1', self.full_ip_list = [('route_interface0', '10.10.2.3', '10.10.0.1',
'255.0.0.0', '10.0.0.0')] '255.0.0.0', '10.0.0.0')]
self.certificate = str(random.random())
self.key = str(random.random())
self.filter_dict = {}
def getInstance(self, computer_id, ): def getInstance(self, computer_id, ):
""" """
...@@ -565,8 +753,7 @@ class InstanceForTest(object): ...@@ -565,8 +753,7 @@ class InstanceForTest(object):
partition = slapos.slap.ComputerPartition(computer_id, self.name) partition = slapos.slap.ComputerPartition(computer_id, self.name)
partition._software_release_document = self.getSoftwareRelease() partition._software_release_document = self.getSoftwareRelease()
partition._requested_state = self.requested_state partition._requested_state = self.requested_state
if getattr(self, 'filter_dict', None): partition._filter_dict = self.filter_dict
partition._filter_dict = self.filter_dict
partition._parameter_dict = {'ip_list': self.ip_list, partition._parameter_dict = {'ip_list': self.ip_list,
'full_ip_list': self.full_ip_list 'full_ip_list': self.full_ip_list
} }
...@@ -624,12 +811,10 @@ class InstanceForTest(object): ...@@ -624,12 +811,10 @@ class InstanceForTest(object):
os.mkdir(certificate_repository_path) os.mkdir(certificate_repository_path)
self.cert_file = os.path.join(certificate_repository_path, self.cert_file = os.path.join(certificate_repository_path,
"%s.crt" % self.name) "%s.crt" % self.name)
self.certificate = str(random.random())
with open(self.cert_file, 'w') as f: with open(self.cert_file, 'w') as f:
f.write(self.certificate) f.write(self.certificate)
self.key_file = os.path.join(certificate_repository_path, self.key_file = os.path.join(certificate_repository_path,
'%s.key' % self.name) '%s.key' % self.name)
self.key = str(random.random())
with open(self.key_file, 'w') as f: with open(self.key_file, 'w') as f:
f.write(self.key) f.write(self.key)
...@@ -735,11 +920,9 @@ class TestSlapgridCPWithMaster(MasterMixin, unittest.TestCase): ...@@ -735,11 +920,9 @@ class TestSlapgridCPWithMaster(MasterMixin, unittest.TestCase):
'software_release', 'worked', '.slapos-retention-lock-delay']) 'software_release', 'worked', '.slapos-retention-lock-delay'])
six.assertCountEqual(self, os.listdir(self.software_root), [instance.software.software_hash]) six.assertCountEqual(self, os.listdir(self.software_root), [instance.software.software_hash])
self.assertEqual(computer.sequence, self.assertEqual(computer.sequence,
['/getFullComputerInformation', ['/api/allDocs/', '/api/get/', '/api/put/'])
'/getComputerPartitionCertificate', self.assertEqual(instance.sequence[1][1]["reported_state"], 'stopped')
'/stoppedComputerPartition']) self.assertEqual(instance.state, 'stopped')
self.assertEqual(open(os.path.join(self.certificate_repository_path, '0.crt')).read(), 'SLAPOS_cert')
self.assertEqual(open(os.path.join(self.certificate_repository_path, '0.key')).read(), 'SLAPOS_key')
def test_one_partition_instance_cfg(self): def test_one_partition_instance_cfg(self):
""" """
...@@ -756,9 +939,9 @@ class TestSlapgridCPWithMaster(MasterMixin, unittest.TestCase): ...@@ -756,9 +939,9 @@ class TestSlapgridCPWithMaster(MasterMixin, unittest.TestCase):
'software_release', 'worked', '.slapos-retention-lock-delay']) 'software_release', 'worked', '.slapos-retention-lock-delay'])
six.assertCountEqual(self, os.listdir(self.software_root), [instance.software.software_hash]) six.assertCountEqual(self, os.listdir(self.software_root), [instance.software.software_hash])
self.assertEqual(computer.sequence, self.assertEqual(computer.sequence,
['/getFullComputerInformation', ['/api/allDocs/', '/api/get/', '/api/put/'])
'/getComputerPartitionCertificate', self.assertEqual(instance.sequence[1][1]["reported_state"], 'stopped')
'/stoppedComputerPartition']) self.assertEqual(instance.state, 'stopped')
def test_one_free_partition(self): def test_one_free_partition(self):
""" """
...@@ -791,9 +974,8 @@ class TestSlapgridCPWithMaster(MasterMixin, unittest.TestCase): ...@@ -791,9 +974,8 @@ class TestSlapgridCPWithMaster(MasterMixin, unittest.TestCase):
self.assertLogContent(wrapper_log, 'Working') self.assertLogContent(wrapper_log, 'Working')
six.assertCountEqual(self, os.listdir(self.software_root), [partition.software.software_hash]) six.assertCountEqual(self, os.listdir(self.software_root), [partition.software.software_hash])
self.assertEqual(computer.sequence, self.assertEqual(computer.sequence,
['/getFullComputerInformation', ['/api/allDocs/', '/api/get/', '/api/put/'])
'/getComputerPartitionCertificate', self.assertEqual(partition.sequence[1][1]["reported_state"], 'started')
'/startedComputerPartition'])
self.assertEqual(partition.state, 'started') self.assertEqual(partition.state, 'started')
def test_one_partition_started_fail(self): def test_one_partition_started_fail(self):
...@@ -811,9 +993,8 @@ class TestSlapgridCPWithMaster(MasterMixin, unittest.TestCase): ...@@ -811,9 +993,8 @@ class TestSlapgridCPWithMaster(MasterMixin, unittest.TestCase):
self.assertLogContent(wrapper_log, 'Working') self.assertLogContent(wrapper_log, 'Working')
six.assertCountEqual(self, os.listdir(self.software_root), [partition.software.software_hash]) six.assertCountEqual(self, os.listdir(self.software_root), [partition.software.software_hash])
self.assertEqual(computer.sequence, self.assertEqual(computer.sequence,
['/getFullComputerInformation', ['/api/allDocs/', '/api/get/', '/api/put/'])
'/getComputerPartitionCertificate', self.assertEqual(partition.sequence[1][1]["reported_state"], 'started')
'/startedComputerPartition'])
self.assertEqual(partition.state, 'started') self.assertEqual(partition.state, 'started')
instance = computer.instance_list[0] instance = computer.instance_list[0]
...@@ -827,14 +1008,11 @@ exit 1 ...@@ -827,14 +1008,11 @@ exit 1
'etc', 'software_release', 'worked', 'etc', 'software_release', 'worked',
'.slapos-retention-lock-delay', '.slapgrid-0-error.log']) '.slapos-retention-lock-delay', '.slapgrid-0-error.log'])
self.assertEqual(computer.sequence, self.assertEqual(computer.sequence,
['/getFullComputerInformation', ['/api/allDocs/', '/api/get/', '/api/put/', '/getHateoasUrl',
'/getComputerPartitionCertificate', '/api/allDocs/', '/api/get/', '/api/put/'])
'/startedComputerPartition', self.assertEqual(instance.sequence[3][1]["reported_state"], 'error')
'/getHateoasUrl',
'/getFullComputerInformation',
'/getComputerPartitionCertificate',
'/softwareInstanceError'])
self.assertEqual(instance.state, 'started') self.assertEqual(instance.state, 'started')
self.assertTrue(instance.error_log.startswith("Failed to run buildout profile in direct"))
def test_one_partition_started_stopped(self): def test_one_partition_started_stopped(self):
computer = self.getTestComputerClass()(self.software_root, self.instance_root) computer = self.getTestComputerClass()(self.software_root, self.instance_root)
...@@ -871,9 +1049,8 @@ chmod 755 etc/run/wrapper ...@@ -871,9 +1049,8 @@ chmod 755 etc/run/wrapper
self.assertLogContent(wrapper_log, 'Working') self.assertLogContent(wrapper_log, 'Working')
six.assertCountEqual(self, os.listdir(self.software_root), [instance.software.software_hash]) six.assertCountEqual(self, os.listdir(self.software_root), [instance.software.software_hash])
self.assertEqual(computer.sequence, self.assertEqual(computer.sequence,
['/getFullComputerInformation', ['/api/allDocs/', '/api/get/', '/api/put/'])
'/getComputerPartitionCertificate', self.assertEqual(instance.sequence[1][1]["reported_state"], 'started')
'/startedComputerPartition'])
self.assertEqual(instance.state, 'started') self.assertEqual(instance.state, 'started')
computer.sequence = [] computer.sequence = []
...@@ -886,9 +1063,8 @@ chmod 755 etc/run/wrapper ...@@ -886,9 +1063,8 @@ chmod 755 etc/run/wrapper
self.assertLogContent(wrapper_log, 'Signal handler called with signal 15') self.assertLogContent(wrapper_log, 'Signal handler called with signal 15')
self.assertEqual(computer.sequence, self.assertEqual(computer.sequence,
['/getHateoasUrl', ['/getHateoasUrl',
'/getFullComputerInformation', '/api/allDocs/', '/api/get/', '/api/put/'])
'/getComputerPartitionCertificate', self.assertEqual(instance.sequence[3][1]["reported_state"], 'stopped')
'/stoppedComputerPartition'])
self.assertEqual(instance.state, 'stopped') self.assertEqual(instance.state, 'stopped')
def test_one_broken_partition_stopped(self): def test_one_broken_partition_stopped(self):
...@@ -932,9 +1108,10 @@ chmod 755 etc/run/wrapper ...@@ -932,9 +1108,10 @@ chmod 755 etc/run/wrapper
six.assertCountEqual(self, os.listdir(self.software_root), six.assertCountEqual(self, os.listdir(self.software_root),
[instance.software.software_hash]) [instance.software.software_hash])
self.assertEqual(computer.sequence, self.assertEqual(computer.sequence,
['/getFullComputerInformation', ['/api/allDocs/',
'/getComputerPartitionCertificate', '/api/get/',
'/startedComputerPartition']) '/api/put/'])
self.assertEqual(instance.sequence[1][1]["reported_state"], 'started')
self.assertEqual(instance.state, 'started') self.assertEqual(instance.state, 'started')
computer.sequence = [] computer.sequence = []
...@@ -951,10 +1128,12 @@ exit 1 ...@@ -951,10 +1128,12 @@ exit 1
self.assertLogContent(wrapper_log, 'Signal handler called with signal 15') self.assertLogContent(wrapper_log, 'Signal handler called with signal 15')
self.assertEqual(computer.sequence, self.assertEqual(computer.sequence,
['/getHateoasUrl', ['/getHateoasUrl',
'/getFullComputerInformation', '/api/allDocs/',
'/getComputerPartitionCertificate', '/api/get/',
'/softwareInstanceError']) '/api/put/'])
self.assertEqual(instance.sequence[3][1]["reported_state"], 'error')
self.assertEqual(instance.state, 'started') self.assertEqual(instance.state, 'started')
self.assertTrue(instance.error_log.startswith("Failed to run buildout profile in direct"))
def test_one_partition_stopped_started(self): def test_one_partition_stopped_started(self):
computer = self.getTestComputerClass()(self.software_root, self.instance_root) computer = self.getTestComputerClass()(self.software_root, self.instance_root)
...@@ -971,9 +1150,10 @@ exit 1 ...@@ -971,9 +1150,10 @@ exit 1
six.assertCountEqual(self, os.listdir(self.software_root), six.assertCountEqual(self, os.listdir(self.software_root),
[instance.software.software_hash]) [instance.software.software_hash])
self.assertEqual(computer.sequence, self.assertEqual(computer.sequence,
['/getFullComputerInformation', ['/api/allDocs/',
'/getComputerPartitionCertificate', '/api/get/',
'/stoppedComputerPartition']) '/api/put/'])
self.assertEqual(instance.sequence[1][1]["reported_state"], 'stopped')
self.assertEqual('stopped', instance.state) self.assertEqual('stopped', instance.state)
instance.requested_state = 'started' instance.requested_state = 'started'
...@@ -990,9 +1170,10 @@ exit 1 ...@@ -990,9 +1170,10 @@ exit 1
self.assertLogContent(wrapper_log, 'Working') self.assertLogContent(wrapper_log, 'Working')
self.assertEqual(computer.sequence, self.assertEqual(computer.sequence,
['/getHateoasUrl', ['/getHateoasUrl',
'/getFullComputerInformation', '/api/allDocs/',
'/getComputerPartitionCertificate', '/api/get/',
'/startedComputerPartition']) '/api/put/'])
self.assertEqual(instance.sequence[3][1]["reported_state"], 'started')
self.assertEqual('started', instance.state) self.assertEqual('started', instance.state)
def test_one_partition_destroyed(self): def test_one_partition_destroyed(self):
...@@ -1016,9 +1197,10 @@ exit 1 ...@@ -1016,9 +1197,10 @@ exit 1
six.assertCountEqual(self, os.listdir(partition), ['.slapgrid', dummy_file_name]) six.assertCountEqual(self, os.listdir(partition), ['.slapgrid', dummy_file_name])
six.assertCountEqual(self, os.listdir(self.software_root), [instance.software.software_hash]) six.assertCountEqual(self, os.listdir(self.software_root), [instance.software.software_hash])
self.assertEqual(computer.sequence, self.assertEqual(computer.sequence,
['/getFullComputerInformation', ['/api/allDocs/',
'/getComputerPartitionCertificate', '/api/get/',
'/stoppedComputerPartition']) '/api/put/'])
self.assertEqual(instance.sequence[1][1]["reported_state"], 'stopped')
self.assertEqual('stopped', instance.state) self.assertEqual('stopped', instance.state)
...@@ -1408,8 +1590,8 @@ class TestSlapgridCPPartitionProcessing(MasterMixin, unittest.TestCase): ...@@ -1408,8 +1590,8 @@ class TestSlapgridCPPartitionProcessing(MasterMixin, unittest.TestCase):
self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS) self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
with open(timestamp_path) as f: with open(timestamp_path) as f:
self.assertIn(timestamp, f.read()) self.assertIn(timestamp, f.read())
self.assertEqual(instance.sequence, self.assertEqual(instance.sequence[1][0],'/api/put/')
['/stoppedComputerPartition']) self.assertEqual(instance.sequence[1][1]["reported_state"], 'stopped')
def test_partition_timestamp_develop(self): def test_partition_timestamp_develop(self):
computer = self.getTestComputerClass()(self.software_root, self.instance_root) computer = self.getTestComputerClass()(self.software_root, self.instance_root)
...@@ -1430,9 +1612,10 @@ class TestSlapgridCPPartitionProcessing(MasterMixin, unittest.TestCase): ...@@ -1430,9 +1612,10 @@ class TestSlapgridCPPartitionProcessing(MasterMixin, unittest.TestCase):
slapgrid.SLAPGRID_SUCCESS) slapgrid.SLAPGRID_SUCCESS)
self.assertEqual(self.launchSlapgrid(), slapgrid.SLAPGRID_SUCCESS) self.assertEqual(self.launchSlapgrid(), slapgrid.SLAPGRID_SUCCESS)
self.assertEqual(instance.sequence, self.assertEqual(instance.sequence[1][0],'/api/put/')
[ '/stoppedComputerPartition', self.assertEqual(instance.sequence[1][1]["reported_state"], 'stopped')
'/stoppedComputerPartition']) self.assertEqual(instance.sequence[3][0],'/api/put/')
self.assertEqual(instance.sequence[3][1]["reported_state"], 'stopped')
def test_partition_old_timestamp(self): def test_partition_old_timestamp(self):
computer = self.getTestComputerClass()(self.software_root, self.instance_root) computer = self.getTestComputerClass()(self.software_root, self.instance_root)
...@@ -1449,8 +1632,8 @@ class TestSlapgridCPPartitionProcessing(MasterMixin, unittest.TestCase): ...@@ -1449,8 +1632,8 @@ class TestSlapgridCPPartitionProcessing(MasterMixin, unittest.TestCase):
six.assertCountEqual(self, os.listdir(self.software_root), [instance.software.software_hash]) six.assertCountEqual(self, os.listdir(self.software_root), [instance.software.software_hash])
instance.timestamp = str(int(timestamp) - 1) instance.timestamp = str(int(timestamp) - 1)
self.assertEqual(self.launchSlapgrid(), slapgrid.SLAPGRID_SUCCESS) self.assertEqual(self.launchSlapgrid(), slapgrid.SLAPGRID_SUCCESS)
self.assertEqual(instance.sequence, self.assertEqual(instance.sequence[1][0],'/api/put/')
[ '/stoppedComputerPartition']) self.assertEqual(instance.sequence[1][1]["reported_state"], 'stopped')
def test_partition_timestamp_new_timestamp(self): def test_partition_timestamp_new_timestamp(self):
computer = self.getTestComputerClass()(self.software_root, self.instance_root) computer = self.getTestComputerClass()(self.software_root, self.instance_root)
...@@ -1468,17 +1651,10 @@ class TestSlapgridCPPartitionProcessing(MasterMixin, unittest.TestCase): ...@@ -1468,17 +1651,10 @@ class TestSlapgridCPPartitionProcessing(MasterMixin, unittest.TestCase):
instance.timestamp = str(int(timestamp) + 1) instance.timestamp = str(int(timestamp) + 1)
self.assertEqual(self.launchSlapgrid(), slapgrid.SLAPGRID_SUCCESS) self.assertEqual(self.launchSlapgrid(), slapgrid.SLAPGRID_SUCCESS)
self.assertEqual(self.launchSlapgrid(), slapgrid.SLAPGRID_SUCCESS) self.assertEqual(self.launchSlapgrid(), slapgrid.SLAPGRID_SUCCESS)
self.assertEqual(computer.sequence, self.assertEqual(
['/getHateoasUrl', [x[0] for x in instance.sequence],
'/getFullComputerInformation', ['/api/get/', '/api/put/', '/api/get/', '/api/put/', '/api/get/']
'/getComputerPartitionCertificate', )
'/stoppedComputerPartition',
'/getHateoasUrl',
'/getFullComputerInformation',
'/getComputerPartitionCertificate',
'/stoppedComputerPartition',
'/getHateoasUrl',
'/getFullComputerInformation'])
def test_partition_timestamp_no_timestamp(self): def test_partition_timestamp_no_timestamp(self):
computer = self.getTestComputerClass()(self.software_root, self.instance_root) computer = self.getTestComputerClass()(self.software_root, self.instance_root)
...@@ -1496,15 +1672,10 @@ class TestSlapgridCPPartitionProcessing(MasterMixin, unittest.TestCase): ...@@ -1496,15 +1672,10 @@ class TestSlapgridCPPartitionProcessing(MasterMixin, unittest.TestCase):
[instance.software.software_hash]) [instance.software.software_hash])
instance.timestamp = None instance.timestamp = None
self.launchSlapgrid() self.launchSlapgrid()
self.assertEqual(computer.sequence, self.assertEqual(
['/getHateoasUrl', [x[0] for x in instance.sequence],
'/getFullComputerInformation', ['/api/get/', '/api/put/', '/api/get/', '/api/put/']
'/getComputerPartitionCertificate', )
'/stoppedComputerPartition',
'/getHateoasUrl',
'/getFullComputerInformation',
'/getComputerPartitionCertificate',
'/stoppedComputerPartition'])
def test_partition_periodicity_remove_timestamp(self): def test_partition_periodicity_remove_timestamp(self):
""" """
...@@ -1569,17 +1740,17 @@ class TestSlapgridCPPartitionProcessing(MasterMixin, unittest.TestCase): ...@@ -1569,17 +1740,17 @@ class TestSlapgridCPPartitionProcessing(MasterMixin, unittest.TestCase):
os.path.join(instance0.partition_path, '.timestamp')) os.path.join(instance0.partition_path, '.timestamp'))
time.sleep(wanted_periodicity + 1) time.sleep(wanted_periodicity + 1)
for instance in computer.instance_list[1:]: for instance in computer.instance_list[1:]:
self.assertEqual(instance.sequence, self.assertEqual(instance.sequence[1][0],'/api/put/')
[ '/stoppedComputerPartition']) self.assertEqual(instance.sequence[1][1]["reported_state"], 'stopped')
time.sleep(1) time.sleep(1)
self.launchSlapgrid() self.launchSlapgrid()
self.assertEqual(instance0.sequence, self.assertEqual(instance0.sequence[1][0],'/api/put/')
[ '/startedComputerPartition', self.assertEqual(instance0.sequence[1][1]["reported_state"], 'started')
'/startedComputerPartition', self.assertEqual(instance0.sequence[3][0],'/api/put/')
]) self.assertEqual(instance0.sequence[3][1]["reported_state"], 'started')
for instance in computer.instance_list[1:]: for instance in computer.instance_list[1:]:
self.assertEqual(instance.sequence, self.assertEqual(instance.sequence[1][0],'/api/put/')
[ '/stoppedComputerPartition']) self.assertEqual(instance.sequence[1][1]["reported_state"], 'stopped')
self.assertGreater( self.assertGreater(
os.path.getmtime(os.path.join(instance0.partition_path, '.timestamp')), os.path.getmtime(os.path.join(instance0.partition_path, '.timestamp')),
last_runtime) last_runtime)
...@@ -1609,16 +1780,17 @@ class TestSlapgridCPPartitionProcessing(MasterMixin, unittest.TestCase): ...@@ -1609,16 +1780,17 @@ class TestSlapgridCPPartitionProcessing(MasterMixin, unittest.TestCase):
os.path.join(instance0.partition_path, '.timestamp')) os.path.join(instance0.partition_path, '.timestamp'))
time.sleep(wanted_periodicity + 1) time.sleep(wanted_periodicity + 1)
for instance in computer.instance_list[1:]: for instance in computer.instance_list[1:]:
self.assertEqual(instance.sequence, self.assertEqual(instance.sequence[1][0],'/api/put/')
[ '/stoppedComputerPartition']) self.assertEqual(instance.sequence[1][1]["reported_state"], 'stopped')
time.sleep(1) time.sleep(1)
self.launchSlapgrid() self.launchSlapgrid()
self.assertEqual(instance0.sequence, self.assertEqual(instance0.sequence[1][0],'/api/put/')
[ '/stoppedComputerPartition', self.assertEqual(instance0.sequence[1][1]["reported_state"], 'stopped')
'/stoppedComputerPartition']) self.assertEqual(instance0.sequence[3][0],'/api/put/')
self.assertEqual(instance0.sequence[3][1]["reported_state"], 'stopped')
for instance in computer.instance_list[1:]: for instance in computer.instance_list[1:]:
self.assertEqual(instance.sequence, self.assertEqual(instance.sequence[1][0],'/api/put/')
[ '/stoppedComputerPartition']) self.assertEqual(instance.sequence[1][1]["reported_state"], 'stopped')
self.assertNotEqual(os.path.getmtime(os.path.join(instance0.partition_path, self.assertNotEqual(os.path.getmtime(os.path.join(instance0.partition_path,
'.timestamp')), '.timestamp')),
last_runtime) last_runtime)
...@@ -1649,17 +1821,19 @@ class TestSlapgridCPPartitionProcessing(MasterMixin, unittest.TestCase): ...@@ -1649,17 +1821,19 @@ class TestSlapgridCPPartitionProcessing(MasterMixin, unittest.TestCase):
os.path.join(instance0.partition_path, '.timestamp')) os.path.join(instance0.partition_path, '.timestamp'))
time.sleep(wanted_periodicity + 1) time.sleep(wanted_periodicity + 1)
for instance in computer.instance_list[1:]: for instance in computer.instance_list[1:]:
self.assertEqual(instance.sequence, self.assertEqual(instance.sequence[1][0],'/api/put/')
[ '/stoppedComputerPartition']) self.assertEqual(instance.sequence[1][1]["reported_state"], 'stopped')
time.sleep(1) time.sleep(1)
instance0.requested_state = 'destroyed' instance0.requested_state = 'destroyed'
self.launchSlapgrid() self.launchSlapgrid()
self.assertEqual(instance0.sequence, self.assertEqual(instance0.sequence[1][0],'/api/put/')
[ '/stoppedComputerPartition', self.assertEqual(instance0.sequence[1][1]["reported_state"], 'stopped')
'/stoppedComputerPartition']) self.assertEqual(instance0.sequence[3][0],'/api/put/')
self.assertEqual(instance0.sequence[3][1]["reported_state"], 'stopped')
for instance in computer.instance_list[1:]: for instance in computer.instance_list[1:]:
self.assertEqual(instance.sequence, self.assertEqual(instance.sequence[1][0],'/api/put/')
[ '/stoppedComputerPartition']) self.assertEqual(instance.sequence[1][1]["reported_state"], 'stopped')
self.assertNotEqual(os.path.getmtime(os.path.join(instance0.partition_path, self.assertNotEqual(os.path.getmtime(os.path.join(instance0.partition_path,
'.timestamp')), '.timestamp')),
last_runtime) last_runtime)
...@@ -1723,10 +1897,15 @@ class TestSlapgridCPPartitionProcessing(MasterMixin, unittest.TestCase): ...@@ -1723,10 +1897,15 @@ class TestSlapgridCPPartitionProcessing(MasterMixin, unittest.TestCase):
instance0.software.setBuildout("""#!/bin/sh instance0.software.setBuildout("""#!/bin/sh
exit 42""") exit 42""")
self.launchSlapgrid() self.launchSlapgrid()
self.assertEqual(instance0.sequence, self.assertTrue(instance0.error_log.startswith("Failed to run buildout profile in direct"))
['/softwareInstanceError']) self.assertEqual(instance1.sequence[1],
self.assertEqual(instance1.sequence, (
[ '/stoppedComputerPartition']) '/api/put/',
{'portal_type': 'Software Instance',
'reference': '1',
'reported_state': 'stopped'}
)
)
def test_one_partition_lacking_software_path_does_not_disturb_others(self): def test_one_partition_lacking_software_path_does_not_disturb_others(self):
""" """
...@@ -1740,10 +1919,15 @@ exit 42""") ...@@ -1740,10 +1919,15 @@ exit 42""")
instance1.software = computer.software_list[1] instance1.software = computer.software_list[1]
shutil.rmtree(instance0.software.srdir) shutil.rmtree(instance0.software.srdir)
self.launchSlapgrid() self.launchSlapgrid()
self.assertEqual(instance0.sequence, self.assertEqual(instance0.sequence[1][0],'/api/put/')
['/softwareInstanceError']) self.assertEqual(instance0.sequence[1][1]["reported_state"], 'error')
self.assertEqual(instance1.sequence, self.assertIn(
[ '/stoppedComputerPartition']) "Software Release http://sr0/ is not present on system",
instance0.sequence[1][1]["status_message"]
)
self.assertIn("Cannot deploy instance.", instance0.sequence[1][1]["status_message"])
self.assertEqual(instance1.sequence[1][0],'/api/put/')
self.assertEqual(instance1.sequence[1][1]["reported_state"], 'stopped')
def test_one_partition_lacking_software_bin_path_does_not_disturb_others(self): def test_one_partition_lacking_software_bin_path_does_not_disturb_others(self):
""" """
...@@ -1757,10 +1941,12 @@ exit 42""") ...@@ -1757,10 +1941,12 @@ exit 42""")
instance1.software = computer.software_list[1] instance1.software = computer.software_list[1]
shutil.rmtree(instance0.software.srbindir) shutil.rmtree(instance0.software.srbindir)
self.launchSlapgrid() self.launchSlapgrid()
self.assertEqual(instance0.sequence, self.assertEqual(instance0.sequence[1][0],'/api/put/')
['/softwareInstanceError']) self.assertEqual(instance0.sequence[1][1]["reported_state"], 'error')
self.assertEqual(instance1.sequence, self.assertIn("No such file or directory", instance0.sequence[1][1]["status_message"])
[ '/stoppedComputerPartition']) self.assertIn("sbin/buildout", instance0.sequence[1][1]["status_message"])
self.assertEqual(instance1.sequence[1][0],'/api/put/')
self.assertEqual(instance1.sequence[1][1]["reported_state"], 'stopped')
def test_one_partition_lacking_path_does_not_disturb_others(self): def test_one_partition_lacking_path_does_not_disturb_others(self):
""" """
...@@ -1774,10 +1960,12 @@ exit 42""") ...@@ -1774,10 +1960,12 @@ exit 42""")
instance1.software = computer.software_list[1] instance1.software = computer.software_list[1]
shutil.rmtree(instance0.partition_path) shutil.rmtree(instance0.partition_path)
self.launchSlapgrid() self.launchSlapgrid()
self.assertEqual(instance0.sequence, self.assertEqual(instance0.sequence[0][0],'/api/put/')
['/softwareInstanceError']) self.assertEqual(instance0.sequence[0][1]["reported_state"], 'error')
self.assertEqual(instance1.sequence, self.assertIn("Partition directory", instance0.sequence[0][1]["status_message"])
[ '/stoppedComputerPartition']) self.assertIn("does not exist", instance0.sequence[0][1]["status_message"])
self.assertEqual(instance1.sequence[1][0],'/api/put/')
self.assertEqual(instance1.sequence[1][1]["reported_state"], 'stopped')
def test_one_partition_buildout_fail_is_correctly_logged(self): def test_one_partition_buildout_fail_is_correctly_logged(self):
""" """
...@@ -1793,7 +1981,8 @@ exit 42""") ...@@ -1793,7 +1981,8 @@ exit 42""")
instance.software.setBuildout("""#!/bin/sh instance.software.setBuildout("""#!/bin/sh
echo %s; echo %s; exit 42""" % (line1, line2)) echo %s; echo %s; exit 42""" % (line1, line2))
self.launchSlapgrid() self.launchSlapgrid()
self.assertEqual(instance.sequence, ['/softwareInstanceError']) self.assertEqual(instance.sequence[1][0], '/api/put/')
self.assertEqual(instance.sequence[1][1]["reported_state"], "error")
# We don't care of actual formatting, we just want to have full log # We don't care of actual formatting, we just want to have full log
self.assertIn(line1, instance.error_log) self.assertIn(line1, instance.error_log)
self.assertIn(line2, instance.error_log) self.assertIn(line2, instance.error_log)
...@@ -1876,7 +2065,8 @@ echo %s; echo %s; exit 42""" % (line1, line2)) ...@@ -1876,7 +2065,8 @@ echo %s; echo %s; exit 42""" % (line1, line2))
['etc', '.slapgrid', 'buildout.cfg', 'software_release', 'worked', '.slapos-retention-lock-delay'] ['etc', '.slapgrid', 'buildout.cfg', 'software_release', 'worked', '.slapos-retention-lock-delay']
) )
self.assertFalse(os.path.exists(promise_ran)) self.assertFalse(os.path.exists(promise_ran))
self.assertFalse(instance.sequence) self.assertEqual(len(instance.sequence), 1)
self.assertEqual(instance.sequence[0][0], "/api/get/")
class TestSlapgridUsageReport(MasterMixin, unittest.TestCase): class TestSlapgridUsageReport(MasterMixin, unittest.TestCase):
...@@ -1902,14 +2092,18 @@ class TestSlapgridUsageReport(MasterMixin, unittest.TestCase): ...@@ -1902,14 +2092,18 @@ class TestSlapgridUsageReport(MasterMixin, unittest.TestCase):
self.assertLogContent(wrapper_log, 'Working') self.assertLogContent(wrapper_log, 'Working')
six.assertCountEqual(self, os.listdir(self.software_root), [instance.software.software_hash]) six.assertCountEqual(self, os.listdir(self.software_root), [instance.software.software_hash])
self.assertEqual(computer.sequence, self.assertEqual(computer.sequence,
['/getFullComputerInformation', ['/api/allDocs/',
'/getComputerPartitionCertificate', '/api/get/',
'/startedComputerPartition']) '/api/put/'])
self.assertEqual(instance.sequence[1][1]["reported_state"], 'started')
self.assertEqual(instance.state, 'started') self.assertEqual(instance.state, 'started')
# Then destroy the instance # Then destroy the instance
computer.sequence = [] computer.sequence = []
instance.sequence = []
instance.requested_state = 'destroyed' instance.requested_state = 'destroyed'
# Reset Cache
self.grid.computer_partition_list = None
self.assertEqual(self.grid.agregateAndSendUsage(), slapgrid.SLAPGRID_SUCCESS) self.assertEqual(self.grid.agregateAndSendUsage(), slapgrid.SLAPGRID_SUCCESS)
# Assert partition directory is empty # Assert partition directory is empty
self.assertInstanceDirectoryListEqual(['0']) self.assertInstanceDirectoryListEqual(['0'])
...@@ -1921,10 +2115,11 @@ class TestSlapgridUsageReport(MasterMixin, unittest.TestCase): ...@@ -1921,10 +2115,11 @@ class TestSlapgridUsageReport(MasterMixin, unittest.TestCase):
self.assertIsNotCreated(wrapper_log) self.assertIsNotCreated(wrapper_log)
self.assertEqual(computer.sequence, self.assertEqual(computer.sequence,
['/getFullComputerInformation', ['/api/allDocs/',
'/getComputerPartitionCertificate', '/api/put/',
'/stoppedComputerPartition', '/api/put/'])
'/destroyedComputerPartition']) self.assertEqual(instance.sequence[0][1]["reported_state"], 'stopped')
self.assertEqual(instance.sequence[1][1]["reported_state"], 'destroyed')
self.assertEqual(instance.state, 'destroyed') self.assertEqual(instance.state, 'destroyed')
def test_partition_list_is_complete_if_empty_destroyed_partition(self): def test_partition_list_is_complete_if_empty_destroyed_partition(self):
...@@ -1942,6 +2137,7 @@ class TestSlapgridUsageReport(MasterMixin, unittest.TestCase): ...@@ -1942,6 +2137,7 @@ class TestSlapgridUsageReport(MasterMixin, unittest.TestCase):
computer.sequence = [] computer.sequence = []
instance.requested_state = 'destroyed' instance.requested_state = 'destroyed'
self.grid.computer_partition_list = None
self.assertEqual(self.grid.agregateAndSendUsage(), slapgrid.SLAPGRID_SUCCESS) self.assertEqual(self.grid.agregateAndSendUsage(), slapgrid.SLAPGRID_SUCCESS)
# Assert partition directory is empty # Assert partition directory is empty
self.assertInstanceDirectoryListEqual(['0']) self.assertInstanceDirectoryListEqual(['0'])
...@@ -1952,12 +2148,13 @@ class TestSlapgridUsageReport(MasterMixin, unittest.TestCase): ...@@ -1952,12 +2148,13 @@ class TestSlapgridUsageReport(MasterMixin, unittest.TestCase):
wrapper_log = os.path.join(instance.partition_path, '.0_wrapper.log') wrapper_log = os.path.join(instance.partition_path, '.0_wrapper.log')
self.assertIsNotCreated(wrapper_log) self.assertIsNotCreated(wrapper_log)
self.assertEqual( self.assertEqual(computer.sequence,
computer.sequence, ['/api/allDocs/',
['/getFullComputerInformation', '/api/put/',
'/getComputerPartitionCertificate', '/api/put/'])
'/stoppedComputerPartition', self.assertEqual(instance.sequence[0][1]["reported_state"], 'stopped')
'/destroyedComputerPartition']) self.assertEqual(instance.sequence[1][1]["reported_state"], 'destroyed')
self.assertEqual(instance.state, 'destroyed')
def test_slapgrid_not_destroy_bad_instance(self): def test_slapgrid_not_destroy_bad_instance(self):
""" """
...@@ -1977,33 +2174,32 @@ class TestSlapgridUsageReport(MasterMixin, unittest.TestCase): ...@@ -1977,33 +2174,32 @@ class TestSlapgridUsageReport(MasterMixin, unittest.TestCase):
self.assertLogContent(wrapper_log, 'Working') self.assertLogContent(wrapper_log, 'Working')
six.assertCountEqual(self, os.listdir(self.software_root), [instance.software.software_hash]) six.assertCountEqual(self, os.listdir(self.software_root), [instance.software.software_hash])
self.assertEqual(computer.sequence, self.assertEqual(computer.sequence,
['/getFullComputerInformation', ['/api/allDocs/',
'/getComputerPartitionCertificate', '/api/get/',
'/startedComputerPartition']) '/api/put/'])
self.assertEqual('started', instance.state) self.assertEqual(instance.sequence[1][1]["reported_state"], 'started')
self.assertEqual(instance.state, 'started')
# Then run usage report and see if it is still working # Then run usage report and see if it is still working
computer.sequence = [] computer.sequence = []
self.grid.computer_partition_list = None
self.assertEqual(self.grid.agregateAndSendUsage(), slapgrid.SLAPGRID_SUCCESS) self.assertEqual(self.grid.agregateAndSendUsage(), slapgrid.SLAPGRID_SUCCESS)
# registerComputerPartition will create one more file:
from slapos.slap.slap import COMPUTER_PARTITION_REQUEST_LIST_TEMPLATE_FILENAME
request_list_file = COMPUTER_PARTITION_REQUEST_LIST_TEMPLATE_FILENAME % instance.name
self.assertInstanceDirectoryListEqual(['0']) self.assertInstanceDirectoryListEqual(['0'])
six.assertCountEqual(self, os.listdir(instance.partition_path), six.assertCountEqual(self, os.listdir(instance.partition_path),
['.slapgrid', '.0_wrapper.log', 'buildout.cfg', ['.slapgrid', '.0_wrapper.log', 'buildout.cfg',
'etc', 'software_release', 'worked', 'etc', 'software_release', 'worked',
'.slapos-retention-lock-delay', request_list_file]) '.slapos-retention-lock-delay'])
wrapper_log = os.path.join(instance.partition_path, '.0_wrapper.log') wrapper_log = os.path.join(instance.partition_path, '.0_wrapper.log')
self.assertLogContent(wrapper_log, 'Working') self.assertLogContent(wrapper_log, 'Working')
self.assertInstanceDirectoryListEqual(['0']) self.assertInstanceDirectoryListEqual(['0'])
six.assertCountEqual(self, os.listdir(instance.partition_path), six.assertCountEqual(self, os.listdir(instance.partition_path),
['.slapgrid', '.0_wrapper.log', 'buildout.cfg', ['.slapgrid', '.0_wrapper.log', 'buildout.cfg',
'etc', 'software_release', 'worked', 'etc', 'software_release', 'worked',
'.slapos-retention-lock-delay', request_list_file]) '.slapos-retention-lock-delay'])
wrapper_log = os.path.join(instance.partition_path, '.0_wrapper.log') wrapper_log = os.path.join(instance.partition_path, '.0_wrapper.log')
self.assertLogContent(wrapper_log, 'Working') self.assertLogContent(wrapper_log, 'Working')
self.assertEqual(computer.sequence, self.assertEqual(computer.sequence,
['/getFullComputerInformation']) ['/api/allDocs/'])
self.assertEqual('started', instance.state) self.assertEqual('started', instance.state)
def test_slapgrid_instance_ignore_free_instance(self): def test_slapgrid_instance_ignore_free_instance(self):
...@@ -2023,7 +2219,7 @@ class TestSlapgridUsageReport(MasterMixin, unittest.TestCase): ...@@ -2023,7 +2219,7 @@ class TestSlapgridUsageReport(MasterMixin, unittest.TestCase):
self.assertInstanceDirectoryListEqual(['0']) self.assertInstanceDirectoryListEqual(['0'])
six.assertCountEqual(self, os.listdir(instance.partition_path), []) six.assertCountEqual(self, os.listdir(instance.partition_path), [])
six.assertCountEqual(self, os.listdir(self.software_root), [instance.software.software_hash]) six.assertCountEqual(self, os.listdir(self.software_root), [instance.software.software_hash])
self.assertEqual(computer.sequence, ['/getFullComputerInformation']) self.assertEqual(computer.sequence, ['/api/allDocs/'])
def test_slapgrid_report_ignore_free_instance(self): def test_slapgrid_report_ignore_free_instance(self):
""" """
...@@ -2042,10 +2238,10 @@ class TestSlapgridUsageReport(MasterMixin, unittest.TestCase): ...@@ -2042,10 +2238,10 @@ class TestSlapgridUsageReport(MasterMixin, unittest.TestCase):
self.assertInstanceDirectoryListEqual(['0']) self.assertInstanceDirectoryListEqual(['0'])
six.assertCountEqual(self, os.listdir(instance.partition_path), []) six.assertCountEqual(self, os.listdir(instance.partition_path), [])
six.assertCountEqual(self, os.listdir(self.software_root), [instance.software.software_hash]) six.assertCountEqual(self, os.listdir(self.software_root), [instance.software.software_hash])
self.assertEqual(computer.sequence, ['/getFullComputerInformation']) self.assertEqual(computer.sequence, ['/api/allDocs/'])
class TestSlapgridSoftwareRelease(MasterMixin, unittest.TestCase): class TestSlapgridSoftwareReleaseSlapTool(SlapToolMasterMixin, unittest.TestCase):
fake_waiting_time = 0.05 fake_waiting_time = 0.05
def test_one_software_buildout_fail_is_correctly_logged(self): def test_one_software_buildout_fail_is_correctly_logged(self):
...@@ -2114,6 +2310,95 @@ chmod a-rxw directory ...@@ -2114,6 +2310,95 @@ chmod a-rxw directory
self.launchSlapgridSoftware() self.launchSlapgridSoftware()
self.assertEqual(os.listdir(self.software_root), []) self.assertEqual(os.listdir(self.software_root), [])
class TestSlapgridSoftwareRelease(MasterMixin, unittest.TestCase):
fake_waiting_time = 0.05
def test_one_software_buildout_fail_is_correctly_logged(self):
"""
1. We set up a software using a corrupted buildout
2. It will fail, make sure that whole log is sent to master
"""
computer = ComputerForTest(self.software_root, self.instance_root, 1, 1)
with httmock.HTTMock(computer.request_handler):
software = computer.software_list[0]
line1 = "Nerdy kitten: Can I haz a process crash?"
line2 = "Cedric: Sure, here it is."
software.setBuildout("""#!/bin/sh
echo %s; echo %s; exit 42""" % (line1, line2))
self.launchSlapgridSoftware()
self.assertEqual(
software.sequence,
[
(
'/api/put/',
{
'compute_node_id': self.computer_id,
'portal_type': 'Software Installation',
'reported_state': 'building',
'software_release_uri': software.name,
}
),
(
'/api/put/',
{
'compute_node_id': self.computer_id,
'portal_type': 'Software Installation',
'error_status': software.error_log,
'software_release_uri': software.name,
}
)
]
)
# We don't care of actual formatting, we just want to have full log
self.assertIn(line1, software.error_log)
self.assertIn(line2, software.error_log)
self.assertIn('Failed to run buildout', software.error_log)
def test_software_install_generate_buildout_cfg_with_shared_part_list(self):
computer = ComputerForTest(self.software_root, self.instance_root, 1, 1)
with httmock.HTTMock(computer.request_handler):
software = computer.software_list[0]
# examine the genrated buildout
software.setBuildout("""#!/bin/sh
cat buildout.cfg; exit 1""")
self.launchSlapgridSoftware()
self.assertIn('shared-part-list = %s' % self.shared_parts_root, software.error_log)
def test_remove_software(self):
computer = ComputerForTest(self.software_root, self.instance_root, 1, 1)
with httmock.HTTMock(computer.request_handler):
software = computer.software_list[0]
software.setBuildout("""#!/bin/sh
mkdir directory
touch directory/file
""")
self.launchSlapgridSoftware()
self.assertIn('directory', os.listdir(os.path.join(self.software_root, software.software_hash)))
software.requested_state = 'destroyed'
self.launchSlapgridSoftware()
self.assertEqual(os.listdir(self.software_root), [])
def test_remove_software_chmod(self):
# This software is "hard" to remove, as permissions have been changed
computer = ComputerForTest(self.software_root, self.instance_root, 1, 1)
with httmock.HTTMock(computer.request_handler):
software = computer.software_list[0]
software.setBuildout("""#!/bin/sh
mkdir directory
touch directory/file
chmod a-rxw directory/file
chmod a-rxw directory
""")
self.launchSlapgridSoftware()
self.assertIn('directory', os.listdir(os.path.join(self.software_root, software.software_hash)))
software.requested_state = 'destroyed'
self.launchSlapgridSoftware()
self.assertEqual(os.listdir(self.software_root), [])
class SlapgridInitialization(unittest.TestCase): class SlapgridInitialization(unittest.TestCase):
""" """
...@@ -2397,6 +2682,8 @@ class TestSlapgridDestructionLock(MasterMixin, unittest.TestCase): ...@@ -2397,6 +2682,8 @@ class TestSlapgridDestructionLock(MasterMixin, unittest.TestCase):
))) )))
instance.requested_state = 'destroyed' instance.requested_state = 'destroyed'
# Reset Cache
self.grid.computer_partition_list = None
self.grid.agregateAndSendUsage() self.grid.agregateAndSendUsage()
self.assertTrue(os.path.exists(dummy_instance_file_path)) self.assertTrue(os.path.exists(dummy_instance_file_path))
self.assertTrue(os.path.exists(os.path.join( self.assertTrue(os.path.exists(os.path.join(
...@@ -2850,28 +3137,30 @@ exit 0 ...@@ -2850,28 +3137,30 @@ exit 0
['.slapgrid', '.0_wrapper.log', 'buildout.cfg', ['.slapgrid', '.0_wrapper.log', 'buildout.cfg',
'etc', 'software_release', 'worked', '.slapos-retention-lock-delay']) 'etc', 'software_release', 'worked', '.slapos-retention-lock-delay'])
self.assertEqual(computer.sequence, self.assertEqual(computer.sequence,
['/getFullComputerInformation', ['/api/allDocs/',
'/getComputerPartitionCertificate', '/api/get/',
'/startedComputerPartition']) '/api/put/'])
self.assertEqual(partition.sequence[1][1]["reported_state"], 'started')
self.assertEqual(partition.state, 'started') self.assertEqual(partition.state, 'started')
manager_list = slapmanager.from_config({'manager_list': 'prerm'}) manager_list = slapmanager.from_config({'manager_list': 'prerm'})
self.grid._manager_list = manager_list self.grid._manager_list = manager_list
partition.requested_state = 'destroyed' partition.requested_state = 'destroyed'
self.grid.computer_partition_list = None
self.assertEqual(self.grid.agregateAndSendUsage(), slapgrid.SLAPGRID_SUCCESS) self.assertEqual(self.grid.agregateAndSendUsage(), slapgrid.SLAPGRID_SUCCESS)
# Assert partition directory is not destroyed (pre-delete is running) # Assert partition directory is not destroyed (pre-delete is running)
self.assertInstanceDirectoryListEqual(['0']) self.assertInstanceDirectoryListEqual(['0'])
six.assertCountEqual(self, os.listdir(partition.partition_path), six.assertCountEqual(self, os.listdir(partition.partition_path),
['.slapgrid', '.0_wrapper.log', 'buildout.cfg', ['.slapgrid', '.0_wrapper.log', 'buildout.cfg',
'etc', 'software_release', 'worked', '.slapos-retention-lock-delay', 'etc', 'software_release', 'worked', '.slapos-retention-lock-delay',
'.0-prerm_slapos_pre_delete.log', '.slapos-report-wait-service-list', '.0-prerm_slapos_pre_delete.log', '.slapos-report-wait-service-list'])
'.slapos-request-transaction-0'])
six.assertCountEqual(self, os.listdir(self.software_root), six.assertCountEqual(self, os.listdir(self.software_root),
[partition.software.software_hash]) [partition.software.software_hash])
# wait until the pre-delete script is finished # wait until the pre-delete script is finished
self._wait_prerm_script_finished(partition.partition_path) self._wait_prerm_script_finished(partition.partition_path)
self.grid.computer_partition_list = None
self.assertEqual(self.grid.agregateAndSendUsage(), slapgrid.SLAPGRID_SUCCESS) self.assertEqual(self.grid.agregateAndSendUsage(), slapgrid.SLAPGRID_SUCCESS)
# Assert partition directory is empty # Assert partition directory is empty
self.assertInstanceDirectoryListEqual(['0']) self.assertInstanceDirectoryListEqual(['0'])
...@@ -2902,12 +3191,13 @@ exit 0 ...@@ -2902,12 +3191,13 @@ exit 0
self.grid._manager_list = manager_list self.grid._manager_list = manager_list
partition.requested_state = 'destroyed' partition.requested_state = 'destroyed'
self.grid.computer_partition_list = None
self.assertEqual(self.grid.agregateAndSendUsage(), slapgrid.SLAPGRID_SUCCESS) self.assertEqual(self.grid.agregateAndSendUsage(), slapgrid.SLAPGRID_SUCCESS)
# Assert partition directory is not destroyed (retention-delay-lock) # Assert partition directory is not destroyed (retention-delay-lock)
six.assertCountEqual(self, os.listdir(partition.partition_path), six.assertCountEqual(self, os.listdir(partition.partition_path),
['.slapgrid', 'buildout.cfg', 'etc', 'software_release', ['.slapgrid', 'buildout.cfg', 'etc', 'software_release',
'worked', '.slapos-retention-lock-delay', 'worked', '.slapos-retention-lock-delay',
'.slapos-retention-lock-date', '.slapos-request-transaction-0']) '.slapos-retention-lock-date'])
self.assertTrue(os.path.exists(pre_delete_script)) self.assertTrue(os.path.exists(pre_delete_script))
self.assertTrue(os.path.exists(os.path.join( self.assertTrue(os.path.exists(os.path.join(
partition.partition_path, partition.partition_path,
...@@ -2915,6 +3205,7 @@ exit 0 ...@@ -2915,6 +3205,7 @@ exit 0
))) )))
time.sleep(1) time.sleep(1)
self.grid.computer_partition_list = None
self.assertEqual(self.grid.agregateAndSendUsage(), slapgrid.SLAPGRID_SUCCESS) self.assertEqual(self.grid.agregateAndSendUsage(), slapgrid.SLAPGRID_SUCCESS)
# Assert partition directory is not destroyed (pre-delete is running) # Assert partition directory is not destroyed (pre-delete is running)
...@@ -2922,7 +3213,7 @@ exit 0 ...@@ -2922,7 +3213,7 @@ exit 0
['.slapgrid', 'buildout.cfg', 'etc', 'software_release', ['.slapgrid', 'buildout.cfg', 'etc', 'software_release',
'worked', '.slapos-retention-lock-delay', '.slapos-retention-lock-date', 'worked', '.slapos-retention-lock-delay', '.slapos-retention-lock-date',
'.0-prerm_slapos_pre_delete.log', '.slapos-report-wait-service-list', '.0-prerm_slapos_pre_delete.log', '.slapos-report-wait-service-list',
'.slapos-request-transaction-0']) ])
# wait until the pre-delete script is finished # wait until the pre-delete script is finished
self._wait_prerm_script_finished(partition.partition_path) self._wait_prerm_script_finished(partition.partition_path)
...@@ -2948,11 +3239,12 @@ exit 0 ...@@ -2948,11 +3239,12 @@ exit 0
self.grid._manager_list = manager_list self.grid._manager_list = manager_list
partition.requested_state = 'destroyed' partition.requested_state = 'destroyed'
self.grid.computer_partition_list = None
self.assertEqual(self.grid.agregateAndSendUsage(), slapgrid.SLAPGRID_SUCCESS) self.assertEqual(self.grid.agregateAndSendUsage(), slapgrid.SLAPGRID_SUCCESS)
# Assert partition directory is not destroyed (pre-delete is running) # Assert partition directory is not destroyed (pre-delete is running)
six.assertCountEqual(self, os.listdir(partition.partition_path), six.assertCountEqual(self, os.listdir(partition.partition_path),
['.slapgrid', 'buildout.cfg', 'etc', 'software_release', ['.slapgrid', 'buildout.cfg', 'etc', 'software_release',
'worked', '.slapos-retention-lock-delay', '.slapos-request-transaction-0', 'worked', '.slapos-retention-lock-delay',
'.0-prerm_slapos_pre_delete.log', '.slapos-report-wait-service-list']) '.0-prerm_slapos_pre_delete.log', '.slapos-report-wait-service-list'])
# wait until the pre-delete script is finished # wait until the pre-delete script is finished
...@@ -2961,6 +3253,7 @@ exit 0 ...@@ -2961,6 +3253,7 @@ exit 0
# the script is well finished... # the script is well finished...
self.assertTrue("finished prerm script." in f.read()) self.assertTrue("finished prerm script." in f.read())
self.grid.computer_partition_list = None
self.assertEqual(self.grid.agregateAndSendUsage(), slapgrid.SLAPGRID_SUCCESS) self.assertEqual(self.grid.agregateAndSendUsage(), slapgrid.SLAPGRID_SUCCESS)
# Assert partition directory is empty # Assert partition directory is empty
self.assertInstanceDirectoryListEqual(['0']) self.assertInstanceDirectoryListEqual(['0'])
...@@ -2983,11 +3276,12 @@ exit 0 ...@@ -2983,11 +3276,12 @@ exit 0
self.grid._manager_list = manager_list self.grid._manager_list = manager_list
partition.requested_state = 'destroyed' partition.requested_state = 'destroyed'
self.grid.computer_partition_list = None
self.assertEqual(self.grid.agregateAndSendUsage(), slapgrid.SLAPGRID_SUCCESS) self.assertEqual(self.grid.agregateAndSendUsage(), slapgrid.SLAPGRID_SUCCESS)
# Assert partition directory is not destroyed (pre-delete is running) # Assert partition directory is not destroyed (pre-delete is running)
six.assertCountEqual(self, os.listdir(partition.partition_path), six.assertCountEqual(self, os.listdir(partition.partition_path),
['.slapgrid', 'buildout.cfg', 'etc', 'software_release', ['.slapgrid', 'buildout.cfg', 'etc', 'software_release',
'worked', '.slapos-retention-lock-delay', '.slapos-request-transaction-0', 'worked', '.slapos-retention-lock-delay',
'.0-prerm_slapos_pre_delete.log', '.slapos-report-wait-service-list']) '.0-prerm_slapos_pre_delete.log', '.slapos-report-wait-service-list'])
stat_info = os.stat(partition.partition_path) stat_info = os.stat(partition.partition_path)
...@@ -3013,6 +3307,7 @@ exit 0 ...@@ -3013,6 +3307,7 @@ exit 0
# wait until the pre-delete script is finished # wait until the pre-delete script is finished
self._wait_prerm_script_finished(partition.partition_path) self._wait_prerm_script_finished(partition.partition_path)
self.grid.computer_partition_list = None
self.assertEqual(self.grid.agregateAndSendUsage(), slapgrid.SLAPGRID_SUCCESS) self.assertEqual(self.grid.agregateAndSendUsage(), slapgrid.SLAPGRID_SUCCESS)
# Assert partition directory is empty # Assert partition directory is empty
self.assertInstanceDirectoryListEqual(['0']) self.assertInstanceDirectoryListEqual(['0'])
...@@ -3056,6 +3351,73 @@ fdleak() { ...@@ -3056,6 +3351,73 @@ fdleak() {
: >&5 && fdleak 5 : >&5 && fdleak 5
: >&6 && fdleak 6 : >&6 && fdleak 6
echo "file descriptors: ok"
exit 1 # do not proceed trying to use this software
""")
self.launchSlapgridSoftware()
self.assertEqual(
software.sequence,
[
(
'/api/put/',
{
'compute_node_id': self.computer_id,
'portal_type': 'Software Installation',
'reported_state': 'building',
'software_release_uri': software.name,
}
),
(
'/api/put/',
{
'compute_node_id': self.computer_id,
'portal_type': 'Software Installation',
'error_status': software.error_log,
'software_release_uri': software.name,
}
)
]
)
self.assertNotIn("file descriptors: leaked", software.error_log)
self.assertIn("file descriptors: ok", software.error_log)
class TestSlapgridNoFDLeakSlapTool(SlapToolMasterMixin, unittest.TestCase):
def test_no_fd_leak(self):
filev = []
try:
# open some file descriptors
for i in range(4):
f = open(os.devnull)
filev.append(f)
self.assertGreater(f.fileno(), 2)
# 'node software' with check that buildout does not see opened files
self._test_no_fd_leak()
finally:
for f in filev:
f.close()
def _test_no_fd_leak(self):
computer = ComputerForTest(self.software_root, self.instance_root, 1, 1)
with httmock.HTTMock(computer.request_handler):
software = computer.software_list[0]
software.setBuildout("""#!/bin/bash
fdleak() {
echo "file descriptors: leaked:" "$@"
exit 1
}
# https://unix.stackexchange.com/a/206848
: >&3 && fdleak 3
: >&4 && fdleak 4
: >&5 && fdleak 5
: >&6 && fdleak 6
echo "file descriptors: ok" echo "file descriptors: ok"
exit 1 # do not proceed trying to use this software exit 1 # do not proceed trying to use this software
""") """)
...@@ -3098,9 +3460,8 @@ class TestSlapgridWithPortRedirection(MasterMixin, unittest.TestCase): ...@@ -3098,9 +3460,8 @@ class TestSlapgridWithPortRedirection(MasterMixin, unittest.TestCase):
self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS) self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
self.assertEqual(self.computer.sequence, self.assertEqual(self.computer.sequence,
['/getFullComputerInformation', ['/api/allDocs/', '/api/get/', '/api/put/'])
'/getComputerPartitionCertificate', self.assertEqual(self.partition.sequence[1][1]["reported_state"], 'started')
'/startedComputerPartition'])
self.assertEqual(self.partition.state, 'started') self.assertEqual(self.partition.state, 'started')
def test_simple_port_redirection(self): def test_simple_port_redirection(self):
...@@ -3174,11 +3535,13 @@ class TestSlapgridWithPortRedirection(MasterMixin, unittest.TestCase): ...@@ -3174,11 +3535,13 @@ class TestSlapgridWithPortRedirection(MasterMixin, unittest.TestCase):
self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS) self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
self.assertEqual(self.computer.sequence, self.assertEqual(self.computer.sequence,
['/getFullComputerInformation', ['/api/allDocs/',
'/getComputerPartitionCertificate', '/api/get/',
'/startedComputerPartition', '/api/put/',
'/getComputerPartitionCertificate', '/api/get/',
'/startedComputerPartition']) '/api/put/'])
self.assertEqual(self.partition.sequence[1][1]["reported_state"], 'started')
self.assertEqual(self.partition.sequence[3][1]["reported_state"], 'started')
self.assertEqual(self.partition.state, 'started') self.assertEqual(self.partition.state, 'started')
# Check the socat command # Check the socat command
...@@ -3582,6 +3945,7 @@ class TestSlapgridWithWhitelistfirewall(MasterMixin, unittest.TestCase): ...@@ -3582,6 +3945,7 @@ class TestSlapgridWithWhitelistfirewall(MasterMixin, unittest.TestCase):
self.partition.requested_state = 'started' self.partition.requested_state = 'started'
self.partition.software.setBuildout(WRAPPER_CONTENT) self.partition.software.setBuildout(WRAPPER_CONTENT)
self.grid.computer_partition_list = None
self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS) self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
self.assertEqual( self.assertEqual(
...@@ -3600,6 +3964,7 @@ class TestSlapgridWithWhitelistfirewall(MasterMixin, unittest.TestCase): ...@@ -3600,6 +3964,7 @@ class TestSlapgridWithWhitelistfirewall(MasterMixin, unittest.TestCase):
self.partition.requested_state = 'started' self.partition.requested_state = 'started'
self.partition.software.setBuildout(WRAPPER_CONTENT) self.partition.software.setBuildout(WRAPPER_CONTENT)
self.grid.computer_partition_list = None
self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS) self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
self.assertEqual( self.assertEqual(
...@@ -3624,6 +3989,7 @@ class TestSlapgridWithWhitelistfirewall(MasterMixin, unittest.TestCase): ...@@ -3624,6 +3989,7 @@ class TestSlapgridWithWhitelistfirewall(MasterMixin, unittest.TestCase):
self.partition.requested_state = 'started' self.partition.requested_state = 'started'
self.partition.software.setBuildout(WRAPPER_CONTENT) self.partition.software.setBuildout(WRAPPER_CONTENT)
self.grid.computer_partition_list = None
self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS) self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
self.assertEqual( self.assertEqual(
...@@ -3708,6 +4074,7 @@ class TestSlapgridWithWhitelistfirewall(MasterMixin, unittest.TestCase): ...@@ -3708,6 +4074,7 @@ class TestSlapgridWithWhitelistfirewall(MasterMixin, unittest.TestCase):
self.partition.requested_state = 'destroyed' self.partition.requested_state = 'destroyed'
self.partition.software.setBuildout(WRAPPER_CONTENT) self.partition.software.setBuildout(WRAPPER_CONTENT)
self.grid.computer_partition_list = None
self.assertEqual(self.grid.agregateAndSendUsage(), slapgrid.SLAPGRID_SUCCESS) self.assertEqual(self.grid.agregateAndSendUsage(), slapgrid.SLAPGRID_SUCCESS)
self.assertEqual( self.assertEqual(
...@@ -3764,9 +4131,8 @@ class TestSlapgridManagerLifecycle(MasterMixin, unittest.TestCase): ...@@ -3764,9 +4131,8 @@ class TestSlapgridManagerLifecycle(MasterMixin, unittest.TestCase):
self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS) self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
self.assertEqual(self.computer.sequence, self.assertEqual(self.computer.sequence,
['/getFullComputerInformation', ['/api/allDocs/', '/api/get/', '/api/put/'])
'/getComputerPartitionCertificate', self.assertEqual(partition.sequence[1][1]["reported_state"], 'started')
'/startedComputerPartition'])
self.assertEqual(partition.state, 'started') self.assertEqual(partition.state, 'started')
self.assertEqual(self.manager.sequence, self.assertEqual(self.manager.sequence,
......
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