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):
instance_storage_home='',
ipv4_global_network='',
buildout_debug=False,
api_backward_compatibility=False,
):
"""Initialisation of class parameters"""
self.buildout = buildout
......@@ -443,6 +444,7 @@ class Partition(object):
self.software_release_url = software_release_url
self.instance_storage_home = instance_storage_home
self.ipv4_global_network = ipv4_global_network
self.api_backward_compatibility = api_backward_compatibility
self.key_file = ''
self.cert_file = ''
......@@ -489,16 +491,20 @@ class Partition(object):
# Certificate files are unset, skip.
return
try:
partition_certificate = self.computer_partition.getCertificate()
except NotFoundError:
raise NotFoundError('Partition %s is not known by SlapOS Master.' %
self.partition_id)
if self.api_backward_compatibility:
try:
partition_certificate = self.computer_partition["slap_partition"].getCertificate()
self.computer_partition["X509"] = {}
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()
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
if os.path.exists(path):
old_content = open(path).read()
......@@ -582,7 +588,7 @@ class Partition(object):
installs the software partition with the help of buildout
"""
self.logger.info("Installing Computer Partition %s..."
% self.computer_partition.getId())
% self.computer_partition.get("compute_partition_id"))
self.check_free_space()
......@@ -731,7 +737,7 @@ class Partition(object):
if os.path.exists(self.supervisord_partition_configuration_path):
os.unlink(self.supervisord_partition_configuration_path)
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__,
'templates/group_partition_supervisord.conf.in'))
self.supervisor_configuration_group = group_partition_template % {
......@@ -766,22 +772,22 @@ class Partition(object):
"""Asks supervisord to start the instance. If this instance is not
installed, we install it.
"""
partition_id = self.computer_partition.getId()
partition_id = self.computer_partition.get("compute_partition_id")
try:
with self.getSupervisorRPC() as supervisor:
supervisor.startProcessGroup(partition_id, False)
except xmlrpclib.Fault as exc:
if exc.faultString.startswith('BAD_NAME:'):
self.logger.info("Nothing to start on %s..." %
self.computer_partition.getId())
self.computer_partition.get("compute_partition_id"))
else:
raise
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):
"""Asks supervisord to stop the instance."""
partition_id = self.computer_partition.getId()
partition_id = self.computer_partition.get("compute_partition_id")
try:
with self.getSupervisorRPC() as supervisor:
supervisor.stopProcessGroup(partition_id, False)
......@@ -791,13 +797,13 @@ class Partition(object):
else:
raise
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):
"""Destroys the partition and makes it available for subsequent use."
"""
self.logger.info("Destroying Computer Partition %s..."
% self.computer_partition.getId())
% self.computer_partition.get("compute_partition_id"))
self.createRetentionLockDate()
if not self.checkRetentionIsAuthorized():
......
......@@ -366,6 +366,7 @@ class Slapgrid(object):
buildout_debug=False,
shared_part_list='',
force_stop=False,
slapgrid_jio_uri=None,
):
"""Makes easy initialisation of class parameters"""
# Parses arguments
......@@ -400,10 +401,16 @@ class Slapgrid(object):
self.shadir_key_file = shadir_key_file
self.forbid_supervisord_automatic_launch = forbid_supervisord_automatic_launch
self.logger = logger
self.slapgrid_jio_uri = slapgrid_jio_uri
# Creates objects from slap module
self.slap = slapos.slap.slap()
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)
# Defines all needed paths
self.buildout = buildout
......@@ -419,6 +426,7 @@ class Slapgrid(object):
if computer_partition_filter_list is not None:
self.computer_partition_filter_list = \
computer_partition_filter_list.split(",")
self.computer_partition_list = None
self.maximum_periodicity = maximum_periodicity
self.software_min_free_space = software_min_free_space
self.instance_min_free_space = instance_min_free_space
......@@ -553,22 +561,62 @@ stderr_logfile_backups=1
launchSupervisord(instance_root=self.instance_root, logger=self.logger)
def getComputerPartitionList(self):
try:
return self.computer.getComputerPartitionList()
except socket.error as exc:
self.logger.fatal(exc)
raise
if self.computer_partition_list is None:
if not self.api_backward_compatibility:
self.computer_partition_list = self.slap.jio_api_connector.allDocs({
"portal_type": "Software Instance",
"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):
"""Return the computer partitions that should be processed.
"""
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(
cp_id_list, self.computer_partition_filter_list)
busy_cp_list = self.FilterComputerPartitionList(cp_list)
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
def processSoftwareReleaseList(self):
......@@ -578,10 +626,27 @@ stderr_logfile_backups=1
self.logger.info('Processing software releases...')
# Boolean to know if every instance has correctly been deployed
clean_run = True
for software_release in self.computer.getSoftwareReleaseList():
state = software_release.getState()
if not self.api_backward_compatibility:
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:
software_release_uri = software_release.getURI()
software_release_uri = software_release["software_release_uri"]
url_hash = md5digest(software_release_uri)
software_path = os.path.join(self.software_root, url_hash)
software = Software(url=software_release_uri,
......@@ -623,7 +688,15 @@ stderr_logfile_backups=1
url_hash in self.software_release_filter_list or
url_hash in (md5digest(uri) for uri in self.software_release_filter_list)):
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:
pass
software.install()
......@@ -640,14 +713,32 @@ stderr_logfile_backups=1
manager.softwareTearDown(software)
# Send log before exiting
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
# Buildout failed: send log but don't print it to output (already done)
except BuildoutFailedError as exc:
clean_run = False
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):
raise
except Exception:
......@@ -656,17 +747,43 @@ stderr_logfile_backups=1
# For everything else: log it, send it, continue.
except 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
else:
if state == 'available':
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):
pass
elif state == 'destroyed':
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):
self.logger.exception('')
self.logger.info('Finished software releases.')
......@@ -764,7 +881,7 @@ stderr_logfile_backups=1
return PromiseLauncher(config=promise_config, logger=self.logger).run()
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_path = os.path.join(self.instance_root,
partition_id,
......@@ -773,9 +890,16 @@ stderr_logfile_backups=1
if os.path.exists(transaction_file_path):
with open(transaction_file_path, 'r') as tf:
try:
computer_partition.setComputerPartitionRelatedInstanceList(
[reference for reference in tf.read().split('\n') if reference]
)
if not self.api_backward_compatibility:
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:
# Master doesn't implement this feature ?
self.logger.warning("NotFoundError: %s. \nCannot send requested instance "\
......@@ -986,7 +1110,23 @@ stderr_logfile_backups=1
elif valid_ipv6(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:
if valid_ipv4(ip):
if not ip in ipv4_list:
......@@ -995,7 +1135,7 @@ stderr_logfile_backups=1
if not ip in ipv6_list:
hosting_ipv6_list.append(ip)
filter_dict = getattr(computer_partition, '_filter_dict', None)
filter_dict = computer_partition.get('sla_parameters', None)
extra_list = []
accept_ip_list = []
if filter_dict is not None:
......@@ -1015,11 +1155,11 @@ stderr_logfile_backups=1
for ip in ipv4_list:
cmd_list = getFirewallRules(ip, hosting_ipv4_list,
source_ipv4_list, ip_type='ipv4')
self._checkAddFirewallRules(computer_partition.getId(),
self._checkAddFirewallRules(computer_partition.get("compute_partition_id"),
cmd_list, add=add_rules)
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
if partition_access_status and partition_access_status.startswith("#error"):
status_error = True
......@@ -1031,17 +1171,24 @@ stderr_logfile_backups=1
self.logger.error(e)
if partition_access_status is None or not status_error:
local_partition._updateCertificate()
computer_partition.error(e, logger=self.logger)
self.sendPartitionError(computer_partition, e, logger=self.logger)
else:
if partition_access_status is None or status_error:
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):
"""
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
# Those values should not be None or empty string or any falsy value
......@@ -1050,12 +1197,7 @@ stderr_logfile_backups=1
instance_path = os.path.join(self.instance_root, computer_partition_id)
os.environ['SLAPGRID_INSTANCE_ROOT'] = self.instance_root
try:
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
software_url = computer_partition.get("software_release_uri")
try:
software_path = os.path.join(self.software_root, md5digest(software_url))
......@@ -1064,7 +1206,7 @@ stderr_logfile_backups=1
# Try to process it anyway, it may need to be deleted.
software_path = None
computer_partition_state = computer_partition.getState()
computer_partition_state = computer_partition.get("state")
local_partition = Partition(
software_path=software_path,
......@@ -1082,10 +1224,11 @@ stderr_logfile_backups=1
buildout=self.buildout,
buildout_debug=self.buildout_debug,
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_storage_home=self.instance_storage_home,
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)
......@@ -1101,7 +1244,7 @@ stderr_logfile_backups=1
"""
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
# Those values should not be None or empty string or any falsy value
......@@ -1125,20 +1268,14 @@ stderr_logfile_backups=1
instance_path,
COMPUTER_PARTITION_TIMESTAMP_FILENAME
)
parameter_dict = computer_partition.getInstanceParameterDict()
timestamp = parameter_dict.get('timestamp')
timestamp = computer_partition.get("processing_timestamp")
error_output_file = os.path.join(
instance_path,
COMPUTER_PARTITION_INSTALL_ERROR_FILENAME % computer_partition_id
)
try:
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
software_url = computer_partition.get("software_release_uri")
try:
software_path = os.path.join(self.software_root, md5digest(software_url))
except TypeError:
......@@ -1146,7 +1283,7 @@ stderr_logfile_backups=1
# Try to process it anyway, it may need to be deleted.
software_path = None
computer_partition_state = computer_partition.getState()
computer_partition_state = computer_partition.get("state")
periodicity = self.maximum_periodicity
if software_path:
periodicity_path = os.path.join(software_path, 'periodicity')
......@@ -1174,7 +1311,7 @@ stderr_logfile_backups=1
buildout=self.buildout,
buildout_debug=self.buildout_debug,
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_storage_home=self.instance_storage_home,
ipv4_global_network=self.ipv4_global_network,
......@@ -1254,7 +1391,7 @@ stderr_logfile_backups=1
local_partition._updateCertificate()
# 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)
if os.path.exists(error_output_file):
......@@ -1262,7 +1399,7 @@ stderr_logfile_backups=1
partition_ip_list = full_hosting_ip_list = []
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', [])
if computer_partition_state == COMPUTER_PARTITION_STARTED_STATE:
......@@ -1276,7 +1413,14 @@ stderr_logfile_backups=1
partition_ip_list)
if not self.force_stop:
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)
elif computer_partition_state == COMPUTER_PARTITION_STOPPED_STATE:
try:
......@@ -1290,9 +1434,16 @@ stderr_logfile_backups=1
# Instance has to be stopped even if buildout/reporting is wrong.
local_partition.stop()
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):
computer_partition.error(traceback.format_exc(), logger=self.logger)
self.sendPartitionError(computer_partition, traceback.format_exc(), logger=self.logger)
raise
except Exception:
pass
......@@ -1304,16 +1455,23 @@ stderr_logfile_backups=1
partition_ip_list,
drop_entries=True)
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):
computer_partition.error(traceback.format_exc(), logger=self.logger)
self.sendPartitionError(computer_partition, traceback.format_exc(), logger=self.logger)
raise
except Exception:
pass
else:
error_string = "Computer Partition %r has unsupported state: %s" % \
(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)
except Exception as e:
if not isinstance(e, PromiseError):
......@@ -1349,7 +1507,7 @@ stderr_logfile_backups=1
for computer_partition in computer_partition_list:
try:
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):
raise NotFoundError('Partition directory %s does not exist.' %
computer_partition_path)
......@@ -1358,11 +1516,8 @@ stderr_logfile_backups=1
# partition, and check if it has some Software information.
# XXX-Cedric: Temporary AND ugly solution to check if an instance
# is in the partition. Dangerous because not 100% sure it is empty
computer_partition_state = computer_partition.getState()
try:
software_url = computer_partition.getSoftwareRelease().getURI()
except (NotFoundError, TypeError, NameError):
software_url = None
computer_partition_state = computer_partition.get("state")
software_url = computer_partition.get("software_release_uri")
if computer_partition_state == COMPUTER_PARTITION_DESTROYED_STATE and \
not software_url:
# Exclude files which may come from concurrent processing
......@@ -1380,7 +1535,7 @@ stderr_logfile_backups=1
# Ignore .slapos-resource file dumped by slapformat.
if os.listdir(computer_partition_path) not in empty_partition_listdir:
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
# Everything seems fine
......@@ -1390,7 +1545,7 @@ stderr_logfile_backups=1
# Send log before exiting
except (SystemExit, KeyboardInterrupt):
computer_partition.error(traceback.format_exc(), logger=self.logger)
self.sendPartitionError(computer_partition, traceback.format_exc(), logger=self.logger)
raise
except Exception as exc:
......@@ -1399,7 +1554,7 @@ stderr_logfile_backups=1
# For everything else: log it, send it, continue.
self.logger.exception('')
try:
computer_partition.error(exc, logger=self.logger)
self.sendPartitionError(computer_partition, exc, logger=self.logger)
except (SystemExit, KeyboardInterrupt):
raise
except Exception:
......@@ -1430,20 +1585,25 @@ stderr_logfile_backups=1
# Nothing should raise outside of the current loop iteration, so that
# even if something is terribly wrong while processing an instance, it
# 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:
# Process the partition itself
self.processComputerPartition(computer_partition)
# Send log before exiting
except (SystemExit, KeyboardInterrupt):
computer_partition.error(traceback.format_exc(), logger=self.logger)
self.sendPartitionError(computer_partition, traceback.format_exc(), logger=self.logger)
raise
except PromiseError as exc:
clean_run_promise = False
try:
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))
except (SystemExit, KeyboardInterrupt):
raise
......@@ -1457,7 +1617,7 @@ stderr_logfile_backups=1
# For everything else: log it, send it, continue.
self.logger.exception('')
try:
computer_partition.error(exc, logger=self.logger)
self.sendPartitionError(computer_partition, exc, logger=self.logger)
process_error_partition_list.append((computer_partition, exc))
except (SystemExit, KeyboardInterrupt):
raise
......@@ -1467,9 +1627,8 @@ stderr_logfile_backups=1
def getPartitionType(part):
"""returns the partition type, if known at that point.
"""
try:
return part.getType()
except slapos.slap.ResourceNotReady:
software_type = partition.get("software_type", None)
if software_type is None:
return '(not ready)'
self.logger.info('Finished computer partitions.')
......@@ -1477,11 +1636,11 @@ stderr_logfile_backups=1
if process_error_partition_list:
self.logger.info('Error while processing the following partitions:')
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:
self.logger.info('Error with promises for the following partitions:')
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
if not clean_run:
......@@ -1502,6 +1661,11 @@ stderr_logfile_backups=1
promise_error_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:
# Process the partition itself
self.processPromise(computer_partition)
......@@ -1517,15 +1681,14 @@ stderr_logfile_backups=1
def getPartitionType(part):
"""returns the partition type, if known at that point.
"""
try:
return part.getType()
except slapos.slap.ResourceNotReady:
software_type = partition.get("software_type", None)
if software_type is None:
return '(not ready)'
if promise_error_partition_list:
self.logger.info('Finished computer partitions.')
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
if not clean_run_promise:
......@@ -1618,7 +1781,6 @@ stderr_logfile_backups=1
self.checkEnvironmentAndCreateStructure()
self._launchSupervisord()
slap_computer_usage = self.slap.registerComputer(self.computer_id)
computer_partition_usage_list = []
self.logger.info('Aggregating and sending usage reports...')
......@@ -1648,11 +1810,11 @@ stderr_logfile_backups=1
clean_run = True
# Loop over the different computer partitions
computer_partition_list = self.FilterComputerPartitionList(
slap_computer_usage.getComputerPartitionList())
self.getComputerPartitionList())
for computer_partition in computer_partition_list:
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)
......@@ -1688,18 +1850,18 @@ stderr_logfile_backups=1
failed_script_list.append("Script %r failed." % script)
self.logger.warning('Failed to run %r' % invocation_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
except Exception:
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
report_usage_issue_cp_list = []
for computer_partition in computer_partition_list:
try:
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)
dir_report_list = [os.path.join(instance_path, 'var', 'xml_report'),
os.path.join(self.instance_root, 'var', 'xml_report',
......@@ -1744,7 +1906,7 @@ stderr_logfile_backups=1
# Whatever happens, don't stop processing other instances
except Exception:
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:
self.logger.info('computer_partition_usage_list: %s - %s' %
......@@ -1770,7 +1932,7 @@ stderr_logfile_backups=1
if self.validateXML(usage, computer_consumption_model):
self.logger.info('XML file generated by asXML is valid')
slap_computer_usage.reportUsage(usage)
self.computer.reportUsage(usage)
filename_delete_list.append(filename)
else:
self.logger.info('XML file is invalid %s' % file_path)
......@@ -1790,34 +1952,35 @@ stderr_logfile_backups=1
# We test the XML report before sending it
if self.validateXML(computer_consumption, computer_consumption_model):
self.logger.info('XML file generated by asXML is valid')
slap_computer_usage.reportUsage(computer_consumption)
self.computer.reportUsage(computer_consumption)
else:
self.logger.info('XML file generated by asXML is not valid !')
raise ValueError('XML file generated by asXML is not valid !')
except Exception:
issue = "Cannot report usage for %r: %s" % (
computer_partition.getId(),
computer_partition.get("compute_partition_id"),
traceback.format_exc())
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)
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
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:
software_url = computer_partition.getSoftwareRelease().getURI()
software_path = os.path.join(self.software_root, md5digest(software_url))
except (NotFoundError, TypeError):
software_url = None
except TypeError:
# Problem with instance: SR URI not set.
# Try to process it anyway, it may need to be deleted.
software_path = None
local_partition = Partition(
software_path=software_path,
instance_path=os.path.join(self.instance_root,
computer_partition.getId()),
computer_partition.get("compute_partition_id")),
supervisord_partition_configuration_path=os.path.join(
_getSupervisordConfigurationDirectory(self.instance_root), '%s.conf' %
computer_partition_id),
......@@ -1837,9 +2000,16 @@ stderr_logfile_backups=1
local_partition.stop()
local_partition._updateCertificate()
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):
computer_partition.error(traceback.format_exc(), logger=self.logger)
self.sendPartitionError(computer_partition, traceback.format_exc(), logger=self.logger)
raise
except Exception:
pass
......@@ -1847,9 +2017,9 @@ stderr_logfile_backups=1
for manager in self._manager_list:
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' %
computer_partition.getId())
computer_partition.get("compute_partition_id"))
continue
if self._checkWaitProcessList(local_partition,
state_list=['RUNNING', 'STARTING']):
......@@ -1858,24 +2028,31 @@ stderr_logfile_backups=1
continue
destroyed = local_partition.destroy()
except (SystemExit, KeyboardInterrupt):
computer_partition.error(traceback.format_exc(), logger=self.logger)
self.sendPartitionError(computer_partition, traceback.format_exc(), logger=self.logger)
raise
except Exception:
clean_run = False
self.logger.exception('')
exc = traceback.format_exc()
computer_partition.error(exc, logger=self.logger)
self.sendPartitionError(computer_partition, exc, logger=self.logger)
try:
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:
self.logger.debug('Ignored slap error while trying to inform about '
'destroying not fully configured Computer Partition %r' %
computer_partition.getId())
computer_partition.get("compute_partition_id"))
except ServerError as server_error:
self.logger.debug('Ignored server error while trying to inform about '
'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.')
......
......@@ -82,9 +82,8 @@ class Manager(object):
# Get partitions IPv6 address
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', [])
partition_ip_list = [tup[1] for tup in partition_ip_list]
......
......@@ -741,6 +741,36 @@ class SlapConnectionHelper(ConnectionHelper):
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):
def apiCall(self, path, data):
......@@ -749,7 +779,7 @@ class JioAPIConnectionHelper(ConnectionHelper):
data=json.dumps(data),
headers={'Content-type': 'application/json'},
expect_json_error=True)
return req.json()
return json_loads_byteified(req.text)
def get(self, data):
return self.apiCall(path="get/",
......
......@@ -193,7 +193,10 @@ class BasicMixin(object):
logger=logging.getLogger(),
shared_part_list=self.shared_parts_root,
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
# monkey patch buildout bootstrap
......@@ -378,6 +381,29 @@ class MasterMixin(BasicMixin):
self._unmock_sleep()
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):
"""
......@@ -393,6 +419,7 @@ class ComputerForTest(object):
Will set up instances, software and sequence
"""
self.sequence = []
self.body_sequence = []
self.instance_amount = instance_amount
self.software_amount = software_amount
self.software_root = software_root
......@@ -421,6 +448,163 @@ class ComputerForTest(object):
qs = parse.parse_qs(url.query)
else:
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'
and 'computer_id' in qs):
slap_computer = self.getComputer(qs['computer_id'][0])
......@@ -557,6 +741,10 @@ class InstanceForTest(object):
self.ip_list = [('interface0', '10.0.8.2')]
self.full_ip_list = [('route_interface0', '10.10.2.3', '10.10.0.1',
'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, ):
"""
......@@ -565,8 +753,7 @@ class InstanceForTest(object):
partition = slapos.slap.ComputerPartition(computer_id, self.name)
partition._software_release_document = self.getSoftwareRelease()
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,
'full_ip_list': self.full_ip_list
}
......@@ -624,12 +811,10 @@ class InstanceForTest(object):
os.mkdir(certificate_repository_path)
self.cert_file = os.path.join(certificate_repository_path,
"%s.crt" % self.name)
self.certificate = str(random.random())
with open(self.cert_file, 'w') as f:
f.write(self.certificate)
self.key_file = os.path.join(certificate_repository_path,
'%s.key' % self.name)
self.key = str(random.random())
with open(self.key_file, 'w') as f:
f.write(self.key)
......@@ -735,11 +920,9 @@ class TestSlapgridCPWithMaster(MasterMixin, unittest.TestCase):
'software_release', 'worked', '.slapos-retention-lock-delay'])
six.assertCountEqual(self, os.listdir(self.software_root), [instance.software.software_hash])
self.assertEqual(computer.sequence,
['/getFullComputerInformation',
'/getComputerPartitionCertificate',
'/stoppedComputerPartition'])
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')
['/api/allDocs/', '/api/get/', '/api/put/'])
self.assertEqual(instance.sequence[1][1]["reported_state"], 'stopped')
self.assertEqual(instance.state, 'stopped')
def test_one_partition_instance_cfg(self):
"""
......@@ -756,9 +939,9 @@ class TestSlapgridCPWithMaster(MasterMixin, unittest.TestCase):
'software_release', 'worked', '.slapos-retention-lock-delay'])
six.assertCountEqual(self, os.listdir(self.software_root), [instance.software.software_hash])
self.assertEqual(computer.sequence,
['/getFullComputerInformation',
'/getComputerPartitionCertificate',
'/stoppedComputerPartition'])
['/api/allDocs/', '/api/get/', '/api/put/'])
self.assertEqual(instance.sequence[1][1]["reported_state"], 'stopped')
self.assertEqual(instance.state, 'stopped')
def test_one_free_partition(self):
"""
......@@ -791,9 +974,8 @@ class TestSlapgridCPWithMaster(MasterMixin, unittest.TestCase):
self.assertLogContent(wrapper_log, 'Working')
six.assertCountEqual(self, os.listdir(self.software_root), [partition.software.software_hash])
self.assertEqual(computer.sequence,
['/getFullComputerInformation',
'/getComputerPartitionCertificate',
'/startedComputerPartition'])
['/api/allDocs/', '/api/get/', '/api/put/'])
self.assertEqual(partition.sequence[1][1]["reported_state"], 'started')
self.assertEqual(partition.state, 'started')
def test_one_partition_started_fail(self):
......@@ -811,9 +993,8 @@ class TestSlapgridCPWithMaster(MasterMixin, unittest.TestCase):
self.assertLogContent(wrapper_log, 'Working')
six.assertCountEqual(self, os.listdir(self.software_root), [partition.software.software_hash])
self.assertEqual(computer.sequence,
['/getFullComputerInformation',
'/getComputerPartitionCertificate',
'/startedComputerPartition'])
['/api/allDocs/', '/api/get/', '/api/put/'])
self.assertEqual(partition.sequence[1][1]["reported_state"], 'started')
self.assertEqual(partition.state, 'started')
instance = computer.instance_list[0]
......@@ -827,14 +1008,11 @@ exit 1
'etc', 'software_release', 'worked',
'.slapos-retention-lock-delay', '.slapgrid-0-error.log'])
self.assertEqual(computer.sequence,
['/getFullComputerInformation',
'/getComputerPartitionCertificate',
'/startedComputerPartition',
'/getHateoasUrl',
'/getFullComputerInformation',
'/getComputerPartitionCertificate',
'/softwareInstanceError'])
['/api/allDocs/', '/api/get/', '/api/put/', '/getHateoasUrl',
'/api/allDocs/', '/api/get/', '/api/put/'])
self.assertEqual(instance.sequence[3][1]["reported_state"], 'error')
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):
computer = self.getTestComputerClass()(self.software_root, self.instance_root)
......@@ -871,9 +1049,8 @@ chmod 755 etc/run/wrapper
self.assertLogContent(wrapper_log, 'Working')
six.assertCountEqual(self, os.listdir(self.software_root), [instance.software.software_hash])
self.assertEqual(computer.sequence,
['/getFullComputerInformation',
'/getComputerPartitionCertificate',
'/startedComputerPartition'])
['/api/allDocs/', '/api/get/', '/api/put/'])
self.assertEqual(instance.sequence[1][1]["reported_state"], 'started')
self.assertEqual(instance.state, 'started')
computer.sequence = []
......@@ -886,9 +1063,8 @@ chmod 755 etc/run/wrapper
self.assertLogContent(wrapper_log, 'Signal handler called with signal 15')
self.assertEqual(computer.sequence,
['/getHateoasUrl',
'/getFullComputerInformation',
'/getComputerPartitionCertificate',
'/stoppedComputerPartition'])
'/api/allDocs/', '/api/get/', '/api/put/'])
self.assertEqual(instance.sequence[3][1]["reported_state"], 'stopped')
self.assertEqual(instance.state, 'stopped')
def test_one_broken_partition_stopped(self):
......@@ -932,9 +1108,10 @@ chmod 755 etc/run/wrapper
six.assertCountEqual(self, os.listdir(self.software_root),
[instance.software.software_hash])
self.assertEqual(computer.sequence,
['/getFullComputerInformation',
'/getComputerPartitionCertificate',
'/startedComputerPartition'])
['/api/allDocs/',
'/api/get/',
'/api/put/'])
self.assertEqual(instance.sequence[1][1]["reported_state"], 'started')
self.assertEqual(instance.state, 'started')
computer.sequence = []
......@@ -951,10 +1128,12 @@ exit 1
self.assertLogContent(wrapper_log, 'Signal handler called with signal 15')
self.assertEqual(computer.sequence,
['/getHateoasUrl',
'/getFullComputerInformation',
'/getComputerPartitionCertificate',
'/softwareInstanceError'])
'/api/allDocs/',
'/api/get/',
'/api/put/'])
self.assertEqual(instance.sequence[3][1]["reported_state"], 'error')
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):
computer = self.getTestComputerClass()(self.software_root, self.instance_root)
......@@ -971,9 +1150,10 @@ exit 1
six.assertCountEqual(self, os.listdir(self.software_root),
[instance.software.software_hash])
self.assertEqual(computer.sequence,
['/getFullComputerInformation',
'/getComputerPartitionCertificate',
'/stoppedComputerPartition'])
['/api/allDocs/',
'/api/get/',
'/api/put/'])
self.assertEqual(instance.sequence[1][1]["reported_state"], 'stopped')
self.assertEqual('stopped', instance.state)
instance.requested_state = 'started'
......@@ -990,9 +1170,10 @@ exit 1
self.assertLogContent(wrapper_log, 'Working')
self.assertEqual(computer.sequence,
['/getHateoasUrl',
'/getFullComputerInformation',
'/getComputerPartitionCertificate',
'/startedComputerPartition'])
'/api/allDocs/',
'/api/get/',
'/api/put/'])
self.assertEqual(instance.sequence[3][1]["reported_state"], 'started')
self.assertEqual('started', instance.state)
def test_one_partition_destroyed(self):
......@@ -1016,9 +1197,10 @@ exit 1
six.assertCountEqual(self, os.listdir(partition), ['.slapgrid', dummy_file_name])
six.assertCountEqual(self, os.listdir(self.software_root), [instance.software.software_hash])
self.assertEqual(computer.sequence,
['/getFullComputerInformation',
'/getComputerPartitionCertificate',
'/stoppedComputerPartition'])
['/api/allDocs/',
'/api/get/',
'/api/put/'])
self.assertEqual(instance.sequence[1][1]["reported_state"], 'stopped')
self.assertEqual('stopped', instance.state)
......@@ -1408,8 +1590,8 @@ class TestSlapgridCPPartitionProcessing(MasterMixin, unittest.TestCase):
self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
with open(timestamp_path) as f:
self.assertIn(timestamp, f.read())
self.assertEqual(instance.sequence,
['/stoppedComputerPartition'])
self.assertEqual(instance.sequence[1][0],'/api/put/')
self.assertEqual(instance.sequence[1][1]["reported_state"], 'stopped')
def test_partition_timestamp_develop(self):
computer = self.getTestComputerClass()(self.software_root, self.instance_root)
......@@ -1430,9 +1612,10 @@ class TestSlapgridCPPartitionProcessing(MasterMixin, unittest.TestCase):
slapgrid.SLAPGRID_SUCCESS)
self.assertEqual(self.launchSlapgrid(), slapgrid.SLAPGRID_SUCCESS)
self.assertEqual(instance.sequence,
[ '/stoppedComputerPartition',
'/stoppedComputerPartition'])
self.assertEqual(instance.sequence[1][0],'/api/put/')
self.assertEqual(instance.sequence[1][1]["reported_state"], 'stopped')
self.assertEqual(instance.sequence[3][0],'/api/put/')
self.assertEqual(instance.sequence[3][1]["reported_state"], 'stopped')
def test_partition_old_timestamp(self):
computer = self.getTestComputerClass()(self.software_root, self.instance_root)
......@@ -1449,8 +1632,8 @@ class TestSlapgridCPPartitionProcessing(MasterMixin, unittest.TestCase):
six.assertCountEqual(self, os.listdir(self.software_root), [instance.software.software_hash])
instance.timestamp = str(int(timestamp) - 1)
self.assertEqual(self.launchSlapgrid(), slapgrid.SLAPGRID_SUCCESS)
self.assertEqual(instance.sequence,
[ '/stoppedComputerPartition'])
self.assertEqual(instance.sequence[1][0],'/api/put/')
self.assertEqual(instance.sequence[1][1]["reported_state"], 'stopped')
def test_partition_timestamp_new_timestamp(self):
computer = self.getTestComputerClass()(self.software_root, self.instance_root)
......@@ -1468,17 +1651,10 @@ class TestSlapgridCPPartitionProcessing(MasterMixin, unittest.TestCase):
instance.timestamp = str(int(timestamp) + 1)
self.assertEqual(self.launchSlapgrid(), slapgrid.SLAPGRID_SUCCESS)
self.assertEqual(self.launchSlapgrid(), slapgrid.SLAPGRID_SUCCESS)
self.assertEqual(computer.sequence,
['/getHateoasUrl',
'/getFullComputerInformation',
'/getComputerPartitionCertificate',
'/stoppedComputerPartition',
'/getHateoasUrl',
'/getFullComputerInformation',
'/getComputerPartitionCertificate',
'/stoppedComputerPartition',
'/getHateoasUrl',
'/getFullComputerInformation'])
self.assertEqual(
[x[0] for x in instance.sequence],
['/api/get/', '/api/put/', '/api/get/', '/api/put/', '/api/get/']
)
def test_partition_timestamp_no_timestamp(self):
computer = self.getTestComputerClass()(self.software_root, self.instance_root)
......@@ -1496,15 +1672,10 @@ class TestSlapgridCPPartitionProcessing(MasterMixin, unittest.TestCase):
[instance.software.software_hash])
instance.timestamp = None
self.launchSlapgrid()
self.assertEqual(computer.sequence,
['/getHateoasUrl',
'/getFullComputerInformation',
'/getComputerPartitionCertificate',
'/stoppedComputerPartition',
'/getHateoasUrl',
'/getFullComputerInformation',
'/getComputerPartitionCertificate',
'/stoppedComputerPartition'])
self.assertEqual(
[x[0] for x in instance.sequence],
['/api/get/', '/api/put/', '/api/get/', '/api/put/']
)
def test_partition_periodicity_remove_timestamp(self):
"""
......@@ -1569,17 +1740,17 @@ class TestSlapgridCPPartitionProcessing(MasterMixin, unittest.TestCase):
os.path.join(instance0.partition_path, '.timestamp'))
time.sleep(wanted_periodicity + 1)
for instance in computer.instance_list[1:]:
self.assertEqual(instance.sequence,
[ '/stoppedComputerPartition'])
self.assertEqual(instance.sequence[1][0],'/api/put/')
self.assertEqual(instance.sequence[1][1]["reported_state"], 'stopped')
time.sleep(1)
self.launchSlapgrid()
self.assertEqual(instance0.sequence,
[ '/startedComputerPartition',
'/startedComputerPartition',
])
self.assertEqual(instance0.sequence[1][0],'/api/put/')
self.assertEqual(instance0.sequence[1][1]["reported_state"], 'started')
self.assertEqual(instance0.sequence[3][0],'/api/put/')
self.assertEqual(instance0.sequence[3][1]["reported_state"], 'started')
for instance in computer.instance_list[1:]:
self.assertEqual(instance.sequence,
[ '/stoppedComputerPartition'])
self.assertEqual(instance.sequence[1][0],'/api/put/')
self.assertEqual(instance.sequence[1][1]["reported_state"], 'stopped')
self.assertGreater(
os.path.getmtime(os.path.join(instance0.partition_path, '.timestamp')),
last_runtime)
......@@ -1609,16 +1780,17 @@ class TestSlapgridCPPartitionProcessing(MasterMixin, unittest.TestCase):
os.path.join(instance0.partition_path, '.timestamp'))
time.sleep(wanted_periodicity + 1)
for instance in computer.instance_list[1:]:
self.assertEqual(instance.sequence,
[ '/stoppedComputerPartition'])
self.assertEqual(instance.sequence[1][0],'/api/put/')
self.assertEqual(instance.sequence[1][1]["reported_state"], 'stopped')
time.sleep(1)
self.launchSlapgrid()
self.assertEqual(instance0.sequence,
[ '/stoppedComputerPartition',
'/stoppedComputerPartition'])
self.assertEqual(instance0.sequence[1][0],'/api/put/')
self.assertEqual(instance0.sequence[1][1]["reported_state"], 'stopped')
self.assertEqual(instance0.sequence[3][0],'/api/put/')
self.assertEqual(instance0.sequence[3][1]["reported_state"], 'stopped')
for instance in computer.instance_list[1:]:
self.assertEqual(instance.sequence,
[ '/stoppedComputerPartition'])
self.assertEqual(instance.sequence[1][0],'/api/put/')
self.assertEqual(instance.sequence[1][1]["reported_state"], 'stopped')
self.assertNotEqual(os.path.getmtime(os.path.join(instance0.partition_path,
'.timestamp')),
last_runtime)
......@@ -1649,17 +1821,19 @@ class TestSlapgridCPPartitionProcessing(MasterMixin, unittest.TestCase):
os.path.join(instance0.partition_path, '.timestamp'))
time.sleep(wanted_periodicity + 1)
for instance in computer.instance_list[1:]:
self.assertEqual(instance.sequence,
[ '/stoppedComputerPartition'])
self.assertEqual(instance.sequence[1][0],'/api/put/')
self.assertEqual(instance.sequence[1][1]["reported_state"], 'stopped')
time.sleep(1)
instance0.requested_state = 'destroyed'
self.launchSlapgrid()
self.assertEqual(instance0.sequence,
[ '/stoppedComputerPartition',
'/stoppedComputerPartition'])
self.assertEqual(instance0.sequence[1][0],'/api/put/')
self.assertEqual(instance0.sequence[1][1]["reported_state"], 'stopped')
self.assertEqual(instance0.sequence[3][0],'/api/put/')
self.assertEqual(instance0.sequence[3][1]["reported_state"], 'stopped')
for instance in computer.instance_list[1:]:
self.assertEqual(instance.sequence,
[ '/stoppedComputerPartition'])
self.assertEqual(instance.sequence[1][0],'/api/put/')
self.assertEqual(instance.sequence[1][1]["reported_state"], 'stopped')
self.assertNotEqual(os.path.getmtime(os.path.join(instance0.partition_path,
'.timestamp')),
last_runtime)
......@@ -1723,10 +1897,15 @@ class TestSlapgridCPPartitionProcessing(MasterMixin, unittest.TestCase):
instance0.software.setBuildout("""#!/bin/sh
exit 42""")
self.launchSlapgrid()
self.assertEqual(instance0.sequence,
['/softwareInstanceError'])
self.assertEqual(instance1.sequence,
[ '/stoppedComputerPartition'])
self.assertTrue(instance0.error_log.startswith("Failed to run buildout profile in direct"))
self.assertEqual(instance1.sequence[1],
(
'/api/put/',
{'portal_type': 'Software Instance',
'reference': '1',
'reported_state': 'stopped'}
)
)
def test_one_partition_lacking_software_path_does_not_disturb_others(self):
"""
......@@ -1740,10 +1919,15 @@ exit 42""")
instance1.software = computer.software_list[1]
shutil.rmtree(instance0.software.srdir)
self.launchSlapgrid()
self.assertEqual(instance0.sequence,
['/softwareInstanceError'])
self.assertEqual(instance1.sequence,
[ '/stoppedComputerPartition'])
self.assertEqual(instance0.sequence[1][0],'/api/put/')
self.assertEqual(instance0.sequence[1][1]["reported_state"], 'error')
self.assertIn(
"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):
"""
......@@ -1757,10 +1941,12 @@ exit 42""")
instance1.software = computer.software_list[1]
shutil.rmtree(instance0.software.srbindir)
self.launchSlapgrid()
self.assertEqual(instance0.sequence,
['/softwareInstanceError'])
self.assertEqual(instance1.sequence,
[ '/stoppedComputerPartition'])
self.assertEqual(instance0.sequence[1][0],'/api/put/')
self.assertEqual(instance0.sequence[1][1]["reported_state"], 'error')
self.assertIn("No such file or directory", instance0.sequence[1][1]["status_message"])
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):
"""
......@@ -1774,10 +1960,12 @@ exit 42""")
instance1.software = computer.software_list[1]
shutil.rmtree(instance0.partition_path)
self.launchSlapgrid()
self.assertEqual(instance0.sequence,
['/softwareInstanceError'])
self.assertEqual(instance1.sequence,
[ '/stoppedComputerPartition'])
self.assertEqual(instance0.sequence[0][0],'/api/put/')
self.assertEqual(instance0.sequence[0][1]["reported_state"], 'error')
self.assertIn("Partition directory", instance0.sequence[0][1]["status_message"])
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):
"""
......@@ -1793,7 +1981,8 @@ exit 42""")
instance.software.setBuildout("""#!/bin/sh
echo %s; echo %s; exit 42""" % (line1, line2))
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
self.assertIn(line1, instance.error_log)
self.assertIn(line2, instance.error_log)
......@@ -1876,7 +2065,8 @@ echo %s; echo %s; exit 42""" % (line1, line2))
['etc', '.slapgrid', 'buildout.cfg', 'software_release', 'worked', '.slapos-retention-lock-delay']
)
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):
......@@ -1902,14 +2092,18 @@ class TestSlapgridUsageReport(MasterMixin, unittest.TestCase):
self.assertLogContent(wrapper_log, 'Working')
six.assertCountEqual(self, os.listdir(self.software_root), [instance.software.software_hash])
self.assertEqual(computer.sequence,
['/getFullComputerInformation',
'/getComputerPartitionCertificate',
'/startedComputerPartition'])
['/api/allDocs/',
'/api/get/',
'/api/put/'])
self.assertEqual(instance.sequence[1][1]["reported_state"], 'started')
self.assertEqual(instance.state, 'started')
# Then destroy the instance
computer.sequence = []
instance.sequence = []
instance.requested_state = 'destroyed'
# Reset Cache
self.grid.computer_partition_list = None
self.assertEqual(self.grid.agregateAndSendUsage(), slapgrid.SLAPGRID_SUCCESS)
# Assert partition directory is empty
self.assertInstanceDirectoryListEqual(['0'])
......@@ -1921,10 +2115,11 @@ class TestSlapgridUsageReport(MasterMixin, unittest.TestCase):
self.assertIsNotCreated(wrapper_log)
self.assertEqual(computer.sequence,
['/getFullComputerInformation',
'/getComputerPartitionCertificate',
'/stoppedComputerPartition',
'/destroyedComputerPartition'])
['/api/allDocs/',
'/api/put/',
'/api/put/'])
self.assertEqual(instance.sequence[0][1]["reported_state"], 'stopped')
self.assertEqual(instance.sequence[1][1]["reported_state"], 'destroyed')
self.assertEqual(instance.state, 'destroyed')
def test_partition_list_is_complete_if_empty_destroyed_partition(self):
......@@ -1942,6 +2137,7 @@ class TestSlapgridUsageReport(MasterMixin, unittest.TestCase):
computer.sequence = []
instance.requested_state = 'destroyed'
self.grid.computer_partition_list = None
self.assertEqual(self.grid.agregateAndSendUsage(), slapgrid.SLAPGRID_SUCCESS)
# Assert partition directory is empty
self.assertInstanceDirectoryListEqual(['0'])
......@@ -1952,12 +2148,13 @@ class TestSlapgridUsageReport(MasterMixin, unittest.TestCase):
wrapper_log = os.path.join(instance.partition_path, '.0_wrapper.log')
self.assertIsNotCreated(wrapper_log)
self.assertEqual(
computer.sequence,
['/getFullComputerInformation',
'/getComputerPartitionCertificate',
'/stoppedComputerPartition',
'/destroyedComputerPartition'])
self.assertEqual(computer.sequence,
['/api/allDocs/',
'/api/put/',
'/api/put/'])
self.assertEqual(instance.sequence[0][1]["reported_state"], 'stopped')
self.assertEqual(instance.sequence[1][1]["reported_state"], 'destroyed')
self.assertEqual(instance.state, 'destroyed')
def test_slapgrid_not_destroy_bad_instance(self):
"""
......@@ -1977,33 +2174,32 @@ class TestSlapgridUsageReport(MasterMixin, unittest.TestCase):
self.assertLogContent(wrapper_log, 'Working')
six.assertCountEqual(self, os.listdir(self.software_root), [instance.software.software_hash])
self.assertEqual(computer.sequence,
['/getFullComputerInformation',
'/getComputerPartitionCertificate',
'/startedComputerPartition'])
self.assertEqual('started', instance.state)
['/api/allDocs/',
'/api/get/',
'/api/put/'])
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
computer.sequence = []
self.grid.computer_partition_list = None
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'])
six.assertCountEqual(self, os.listdir(instance.partition_path),
['.slapgrid', '.0_wrapper.log', 'buildout.cfg',
'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')
self.assertLogContent(wrapper_log, 'Working')
self.assertInstanceDirectoryListEqual(['0'])
six.assertCountEqual(self, os.listdir(instance.partition_path),
['.slapgrid', '.0_wrapper.log', 'buildout.cfg',
'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')
self.assertLogContent(wrapper_log, 'Working')
self.assertEqual(computer.sequence,
['/getFullComputerInformation'])
['/api/allDocs/'])
self.assertEqual('started', instance.state)
def test_slapgrid_instance_ignore_free_instance(self):
......@@ -2023,7 +2219,7 @@ class TestSlapgridUsageReport(MasterMixin, unittest.TestCase):
self.assertInstanceDirectoryListEqual(['0'])
six.assertCountEqual(self, os.listdir(instance.partition_path), [])
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):
"""
......@@ -2042,10 +2238,10 @@ class TestSlapgridUsageReport(MasterMixin, unittest.TestCase):
self.assertInstanceDirectoryListEqual(['0'])
six.assertCountEqual(self, os.listdir(instance.partition_path), [])
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
def test_one_software_buildout_fail_is_correctly_logged(self):
......@@ -2114,6 +2310,95 @@ chmod a-rxw directory
self.launchSlapgridSoftware()
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):
"""
......@@ -2397,6 +2682,8 @@ class TestSlapgridDestructionLock(MasterMixin, unittest.TestCase):
)))
instance.requested_state = 'destroyed'
# Reset Cache
self.grid.computer_partition_list = None
self.grid.agregateAndSendUsage()
self.assertTrue(os.path.exists(dummy_instance_file_path))
self.assertTrue(os.path.exists(os.path.join(
......@@ -2850,28 +3137,30 @@ exit 0
['.slapgrid', '.0_wrapper.log', 'buildout.cfg',
'etc', 'software_release', 'worked', '.slapos-retention-lock-delay'])
self.assertEqual(computer.sequence,
['/getFullComputerInformation',
'/getComputerPartitionCertificate',
'/startedComputerPartition'])
['/api/allDocs/',
'/api/get/',
'/api/put/'])
self.assertEqual(partition.sequence[1][1]["reported_state"], 'started')
self.assertEqual(partition.state, 'started')
manager_list = slapmanager.from_config({'manager_list': 'prerm'})
self.grid._manager_list = manager_list
partition.requested_state = 'destroyed'
self.grid.computer_partition_list = None
self.assertEqual(self.grid.agregateAndSendUsage(), slapgrid.SLAPGRID_SUCCESS)
# Assert partition directory is not destroyed (pre-delete is running)
self.assertInstanceDirectoryListEqual(['0'])
six.assertCountEqual(self, os.listdir(partition.partition_path),
['.slapgrid', '.0_wrapper.log', 'buildout.cfg',
'etc', 'software_release', 'worked', '.slapos-retention-lock-delay',
'.0-prerm_slapos_pre_delete.log', '.slapos-report-wait-service-list',
'.slapos-request-transaction-0'])
'.0-prerm_slapos_pre_delete.log', '.slapos-report-wait-service-list'])
six.assertCountEqual(self, os.listdir(self.software_root),
[partition.software.software_hash])
# wait until the pre-delete script is finished
self._wait_prerm_script_finished(partition.partition_path)
self.grid.computer_partition_list = None
self.assertEqual(self.grid.agregateAndSendUsage(), slapgrid.SLAPGRID_SUCCESS)
# Assert partition directory is empty
self.assertInstanceDirectoryListEqual(['0'])
......@@ -2902,12 +3191,13 @@ exit 0
self.grid._manager_list = manager_list
partition.requested_state = 'destroyed'
self.grid.computer_partition_list = None
self.assertEqual(self.grid.agregateAndSendUsage(), slapgrid.SLAPGRID_SUCCESS)
# Assert partition directory is not destroyed (retention-delay-lock)
six.assertCountEqual(self, os.listdir(partition.partition_path),
['.slapgrid', 'buildout.cfg', 'etc', 'software_release',
'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(os.path.join(
partition.partition_path,
......@@ -2915,6 +3205,7 @@ exit 0
)))
time.sleep(1)
self.grid.computer_partition_list = None
self.assertEqual(self.grid.agregateAndSendUsage(), slapgrid.SLAPGRID_SUCCESS)
# Assert partition directory is not destroyed (pre-delete is running)
......@@ -2922,7 +3213,7 @@ exit 0
['.slapgrid', 'buildout.cfg', 'etc', 'software_release',
'worked', '.slapos-retention-lock-delay', '.slapos-retention-lock-date',
'.0-prerm_slapos_pre_delete.log', '.slapos-report-wait-service-list',
'.slapos-request-transaction-0'])
])
# wait until the pre-delete script is finished
self._wait_prerm_script_finished(partition.partition_path)
......@@ -2948,11 +3239,12 @@ exit 0
self.grid._manager_list = manager_list
partition.requested_state = 'destroyed'
self.grid.computer_partition_list = None
self.assertEqual(self.grid.agregateAndSendUsage(), slapgrid.SLAPGRID_SUCCESS)
# Assert partition directory is not destroyed (pre-delete is running)
six.assertCountEqual(self, os.listdir(partition.partition_path),
['.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'])
# wait until the pre-delete script is finished
......@@ -2961,6 +3253,7 @@ exit 0
# the script is well finished...
self.assertTrue("finished prerm script." in f.read())
self.grid.computer_partition_list = None
self.assertEqual(self.grid.agregateAndSendUsage(), slapgrid.SLAPGRID_SUCCESS)
# Assert partition directory is empty
self.assertInstanceDirectoryListEqual(['0'])
......@@ -2983,11 +3276,12 @@ exit 0
self.grid._manager_list = manager_list
partition.requested_state = 'destroyed'
self.grid.computer_partition_list = None
self.assertEqual(self.grid.agregateAndSendUsage(), slapgrid.SLAPGRID_SUCCESS)
# Assert partition directory is not destroyed (pre-delete is running)
six.assertCountEqual(self, os.listdir(partition.partition_path),
['.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'])
stat_info = os.stat(partition.partition_path)
......@@ -3013,6 +3307,7 @@ exit 0
# wait until the pre-delete script is finished
self._wait_prerm_script_finished(partition.partition_path)
self.grid.computer_partition_list = None
self.assertEqual(self.grid.agregateAndSendUsage(), slapgrid.SLAPGRID_SUCCESS)
# Assert partition directory is empty
self.assertInstanceDirectoryListEqual(['0'])
......@@ -3056,6 +3351,73 @@ fdleak() {
: >&5 && fdleak 5
: >&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"
exit 1 # do not proceed trying to use this software
""")
......@@ -3098,9 +3460,8 @@ class TestSlapgridWithPortRedirection(MasterMixin, unittest.TestCase):
self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
self.assertEqual(self.computer.sequence,
['/getFullComputerInformation',
'/getComputerPartitionCertificate',
'/startedComputerPartition'])
['/api/allDocs/', '/api/get/', '/api/put/'])
self.assertEqual(self.partition.sequence[1][1]["reported_state"], 'started')
self.assertEqual(self.partition.state, 'started')
def test_simple_port_redirection(self):
......@@ -3174,11 +3535,13 @@ class TestSlapgridWithPortRedirection(MasterMixin, unittest.TestCase):
self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
self.assertEqual(self.computer.sequence,
['/getFullComputerInformation',
'/getComputerPartitionCertificate',
'/startedComputerPartition',
'/getComputerPartitionCertificate',
'/startedComputerPartition'])
['/api/allDocs/',
'/api/get/',
'/api/put/',
'/api/get/',
'/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')
# Check the socat command
......@@ -3582,6 +3945,7 @@ class TestSlapgridWithWhitelistfirewall(MasterMixin, unittest.TestCase):
self.partition.requested_state = 'started'
self.partition.software.setBuildout(WRAPPER_CONTENT)
self.grid.computer_partition_list = None
self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
self.assertEqual(
......@@ -3600,6 +3964,7 @@ class TestSlapgridWithWhitelistfirewall(MasterMixin, unittest.TestCase):
self.partition.requested_state = 'started'
self.partition.software.setBuildout(WRAPPER_CONTENT)
self.grid.computer_partition_list = None
self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
self.assertEqual(
......@@ -3624,6 +3989,7 @@ class TestSlapgridWithWhitelistfirewall(MasterMixin, unittest.TestCase):
self.partition.requested_state = 'started'
self.partition.software.setBuildout(WRAPPER_CONTENT)
self.grid.computer_partition_list = None
self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
self.assertEqual(
......@@ -3708,6 +4074,7 @@ class TestSlapgridWithWhitelistfirewall(MasterMixin, unittest.TestCase):
self.partition.requested_state = 'destroyed'
self.partition.software.setBuildout(WRAPPER_CONTENT)
self.grid.computer_partition_list = None
self.assertEqual(self.grid.agregateAndSendUsage(), slapgrid.SLAPGRID_SUCCESS)
self.assertEqual(
......@@ -3764,9 +4131,8 @@ class TestSlapgridManagerLifecycle(MasterMixin, unittest.TestCase):
self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
self.assertEqual(self.computer.sequence,
['/getFullComputerInformation',
'/getComputerPartitionCertificate',
'/startedComputerPartition'])
['/api/allDocs/', '/api/get/', '/api/put/'])
self.assertEqual(partition.sequence[1][1]["reported_state"], 'started')
self.assertEqual(partition.state, 'started')
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