Commit 1e037bf0 authored by Rafael Monnerat's avatar Rafael Monnerat

slapos_crm&cloud&pdm: Factorize get to allocation cell like Upgrade

  Move shared code into InstanceTree_getNodeAndAllocationSupplyCellList
  and reuse into CRM and PDM for get the related Allocation Supply.

  This preserve the logic into a single location.

  Include more tests and extend implementation to now support Instance
  Node too.
parent b106cabb
Pipeline #38491 failed with stage
in 0 seconds
from zExceptions import Unauthorized
if REQUEST is not None:
raise Unauthorized
allocation_cell_list = []
instance_tree = context
compute_node = None
root_instance = None
root_instance_list = [
q for q in instance_tree.getSuccessorValueList(portal_type=["Software Instance", "Slave Instance"])
if q.getSlapState() != 'destroy_requested']
if len(root_instance_list) == 1:
root_instance = root_instance_list[0]
partition = root_instance.getAggregateValue()
if partition is not None:
compute_node = partition.getParentValue()
if (root_instance.getPortalType() == 'Slave Instance'):
if (compute_node.getPortalType() == 'Compute Node'):
# Search the instance node linked to this partition
soft_instance = partition.getAggregateRelatedValue(portal_type='Software Instance')
if soft_instance is None:
return (None, allocation_cell_list)
instance_node = soft_instance.getSpecialiseRelatedValue(portal_type='Instance Node')
if instance_node is None:
return (None, allocation_cell_list)
compute_node = instance_node
elif (compute_node.getPortalType() != 'Remote Node'):
return (None, allocation_cell_list)
if software_product is None:
return (compute_node, allocation_cell_list)
person = context.getDestinationSectionValue(checked_permission='Access contents information')
# Search if the product with the same type
# Search only for the main node
allocation_cell_list = software_product.getFollowUpValue().Project_getSoftwareProductPredicateList(
software_product=software_product,
software_product_type=software_type,
software_product_release=software_release,
destination_value=person,
node_value=compute_node,
predicate_portal_type='Allocation Supply Cell'
)
if (compute_node is None) and (root_instance is not None):
# Do not upgrade if there is no instance yet
allocation_cell_node_list = [(x, [y.getPortalType() for y in x.getParentValue().getParentValue().getAggregateValueList()]) for x in allocation_cell_list]
if (root_instance.getPortalType() == 'Slave Instance'):
allocation_cell_list = [x for x, y in allocation_cell_node_list if ("Remote Node" in y) or ("Instance Node" in y)]
elif (root_instance.getPortalType() == 'Software Instance'):
allocation_cell_list = [x for x, y in allocation_cell_node_list if ("Remote Node" in y) or ("Compute Node" in y)]
# Remove duplicated allocation cells
# ie, multiple allocation cells matching the same release/type/node
software_release_uid_dict = {}
not_duplicated_allocation_cell_list = []
for allocation_cell in allocation_cell_list:
# Do not return duplicated release values
software_release_uid = allocation_cell.getSoftwareReleaseValue().getUid()
if software_release_uid not in software_release_uid_dict:
software_release_uid_dict[software_release_uid] = None
not_duplicated_allocation_cell_list.append(allocation_cell)
allocation_cell_list = not_duplicated_allocation_cell_list
return (compute_node, allocation_cell_list)
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>software_product, software_release=None, software_type=None, REQUEST=None</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>InstanceTree_getNodeAndAllocationSupplyCellList</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -44,8 +44,7 @@ error_dict = {
# Since we would like a single ticket per compute node do all at once:
for compute_partition in context.contentValues(portal_type='Compute Partition'):
if compute_partition.getSlapState() == 'busy':
compute_partition_error_dict = compute_partition.ComputePartition_checkAllocationConsistencyState(
known_error_dict=error_dict['compute_node_error_dict'])
compute_partition_error_dict = compute_partition.ComputePartition_checkAllocationConsistencyState()
if compute_partition_error_dict:
error_dict['should_notify'] = True
error_dict['compute_node_error_dict'].update(compute_partition_error_dict)
......@@ -57,16 +56,29 @@ message = """The following contains instances that has Software Releases/Types t
""" % context.getPortalType()
# Sample compute_node_error_dict[software_release_url][software_type] = (instance, compute_partition)
for sofware_release_url in error_dict['compute_node_error_dict']:
message += """ * Software Release: %s
# It includes instance nodes lacking supplies
error_dict_len = len(error_dict['compute_node_error_dict'])
for compute_node in error_dict['compute_node_error_dict']:
compute_node_error_dict = error_dict['compute_node_error_dict'][compute_node]
compute_node_value = portal.restrictedTraverse(compute_node)
if error_dict_len > 1 or compute_node_value.getPortalType() == 'Instance Node':
# Highlight better where it comes from, it may include instance nodes
# lacking supplies.
message += """
%s %s (%s)
""" % (compute_node_value.getTitle(),
compute_node_value.getReference(),
compute_node_value.getPortalType())
for sofware_release_url in compute_node_error_dict:
message += """ * Software Release: %s
""" % sofware_release_url
for sofware_type, value_list in six.iteritems(error_dict['compute_node_error_dict'][sofware_release_url]):
message += """ * Software Type: %s (ie: %s on %s)
""" % (sofware_type, value_list[0].getTitle(), value_list[1].getReference())
for sofware_type, value_list in six.iteritems(compute_node_error_dict[sofware_release_url]):
message += """ * Software Type: %s (ie: %s)
""" % (sofware_type, value_list[0].getTitle())
error_dict['message'] = message
support_request = project.Project_createTicketWithCausality(
ticket_portal_type,
error_dict['ticket_title'],
......
portal = context.getPortalObject()
compute_partition = context
error_dict = {}
if known_error_dict is None:
known_error_dict = {}
compute_node = compute_partition.getParentValue()
assert compute_node.getPortalType() in ['Compute Node', 'Remote Node']
......@@ -20,19 +18,6 @@ for instance in instance_list:
instance_software_release_url = instance.getUrlString()
instance_software_type = instance.getSourceReference()
is_known = False
for _e_dict in [error_dict, known_error_dict]:
if instance_software_release_url in _e_dict:
if instance_software_type in _e_dict[instance_software_release_url]:
# Skip calculate same thing again?
is_known = True
break
# Value was already processed or discovered on some other partition
# so we skip
if is_known:
continue
# Now check allocation supply consistency
instance_tree = instance.getSpecialiseValue(portal_type="Instance Tree")
......@@ -44,33 +29,40 @@ for instance in instance_list:
simulation_state=['started', 'stopped', 'planned', 'confirmed']) is not None:
continue
# Create a temporary instance tree as the instance would be the root
# Instance.
instance_tree_context = instance_tree.asContext(
default_source_reference=instance_software_type,
url_string=instance_software_release_url)
instance_tree_context.setSuccessorValue(instance)
assert instance_tree_context.getSuccessorValue() == instance
project = instance.getFollowUpValue()
assert project is not None, 'Project is None'
allocation_cell_list = []
software_product, software_release, software_type = instance_tree_context.InstanceTree_getSoftwareProduct()
if software_product is not None:
allocation_cell_list = project.Project_getSoftwareProductPredicateList(
software_product=software_product,
software_product_type=software_type,
software_product_release=software_release,
destination_value=instance_tree.getDestinationSectionValue(),
node_value=compute_node,
predicate_portal_type='Allocation Supply Cell'
)
compute_node, allocation_cell_list = instance_tree_context.InstanceTree_getNodeAndAllocationSupplyCellList(
software_product=software_product,
software_release=software_release,
software_type=software_type)
if not allocation_cell_list:
# Sampling of the structure.
# error_dict[software_release_url][software_type] = (instance, compute_partition)
value = (instance, compute_partition)
# Sampling of the structure
# error_dict = {
# compute_node or instance_node or remote_node : {
# software_release_url: {
# software_type: (sample_instance, compute_node)
# }
# }
# }
value = (instance, compute_node)
compute_node_url = compute_node.getRelativeUrl()
if compute_node_url not in error_dict:
error_dict[compute_node.getRelativeUrl()] = {}
if instance_software_release_url not in error_dict:
error_dict[instance_software_release_url] = {}
if instance_software_type not in error_dict[instance_software_release_url]:
error_dict[instance.getUrlString()][instance_software_type] = value
error_dict[compute_node_url][instance_software_release_url] = {}
if instance_software_type not in error_dict[compute_node_url][instance_software_release_url]:
error_dict[compute_node_url][instance.getUrlString()][instance_software_type] = value
return error_dict
......@@ -50,7 +50,7 @@
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>known_error_dict=None</string> </value>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
......
......@@ -74,7 +74,6 @@ class TestSlapOSCrmMonitoringMixin(SlapOSTestCaseMixinWithAbort):
return support_request
class TestSlapOSCrmCheckProjectAllocationConsistencyState(TestSlapOSCrmMonitoringMixin):
launch_caucase = 1
##########################################################################
# slapos_crm_project_allocation_consistency > ComputeNode_checkProjectAllocationConsistencyState
......@@ -149,7 +148,23 @@ class TestSlapOSCrmCheckProjectAllocationConsistencyState(TestSlapOSCrmMonitorin
class TestSlapOSCrmMonitoringCheckAllocationConsistencyState(TestSlapOSCrmMonitoringMixin):
launch_caucase = 1
def assertAllocationErrorDict(self, error_dict, compute_node, partition,
release_variation, type_variation):
self.assertNotEqual({}, error_dict)
self.assertEqual(1, len(error_dict), error_dict)
type_reference = type_variation.getReference()
release_url = release_variation.getUrlString()
_error_dict = error_dict[compute_node.getRelativeUrl()]
self.assertEqual([release_url], _error_dict.keys())
self.assertEqual([type_reference], _error_dict[release_url].keys())
self.assertEqual(partition.getAggregateRelatedValue().getUid(),
_error_dict[release_url][type_reference][0].getUid())
self.assertEqual(compute_node.getUid(),
_error_dict[release_url][type_reference][1].getUid())
#############################################################################
# ComputeNode_checkProjectAllocationConsistencyState > ComputeNode_checkAllocationConsistencyState
......@@ -170,22 +185,8 @@ class TestSlapOSCrmMonitoringCheckAllocationConsistencyState(TestSlapOSCrmMonito
# Double check error dict
error_dict = partition.ComputePartition_checkAllocationConsistencyState()
self.assertNotEqual({}, error_dict)
self.assertEqual(1, len(error_dict))
self.assertEqual([release_variation.getUrlString()], error_dict.keys())
self.assertEqual([type_variation.getReference()],
error_dict[release_variation.getUrlString()].keys())
self.assertEqual(partition.getAggregateRelatedValue().getUid(),
error_dict[release_variation.getUrlString()][type_variation.getReference()][0].getUid())
self.assertEqual(partition.getUid(),
error_dict[release_variation.getUrlString()][type_variation.getReference()][1].getUid())
# Try to see if duplicates entries.
error_dict[release_variation.getUrlString()][type_variation.getReference()] = ('a', 'b')
# Pass a known case dont add more entries
self.assertEqual({}, partition.ComputePartition_checkAllocationConsistencyState(
known_error_dict=error_dict))
self.assertAllocationErrorDict(error_dict, compute_node, partition,
release_variation, type_variation)
ticket_title = "%s has missing allocation supplies." % compute_node.getTitle()
self.assertNotEqual(ticket, None)
self.assertEqual(ticket.getTitle(), ticket_title)
......@@ -195,7 +196,6 @@ class TestSlapOSCrmMonitoringCheckAllocationConsistencyState(TestSlapOSCrmMonito
self.assertEqual(len(event_list), 1)
event = event_list[0]
self.assertIn("The following contains instances that has Software Releases/Types",
event.getTextContent())
......@@ -228,15 +228,8 @@ class TestSlapOSCrmMonitoringCheckAllocationConsistencyState(TestSlapOSCrmMonito
# Double check error dict exists despite ticket isnt created.
error_dict = partition.ComputePartition_checkAllocationConsistencyState()
self.assertNotEqual({}, error_dict)
self.assertEqual(1, len(error_dict))
self.assertEqual([release_variation.getUrlString()], error_dict.keys())
self.assertEqual([type_variation.getReference()],
error_dict[release_variation.getUrlString()].keys())
self.assertEqual(partition.getAggregateRelatedValue().getUid(),
error_dict[release_variation.getUrlString()][type_variation.getReference()][0].getUid())
self.assertEqual(partition.getUid(),
error_dict[release_variation.getUrlString()][type_variation.getReference()][1].getUid())
self.assertAllocationErrorDict(error_dict, compute_node, partition,
release_variation, type_variation)
@simulate('Project_isSupportRequestCreationClosed', '*args, **kwargs', 'return 0')
def test_ComputeNode_checkAllocationConsistencyState_script_ongoingUpgrade(self):
......@@ -299,21 +292,84 @@ class TestSlapOSCrmMonitoringCheckAllocationConsistencyState(TestSlapOSCrmMonito
# Double check error dict
error_dict = partition.ComputePartition_checkAllocationConsistencyState()
self.assertAllocationErrorDict(error_dict, compute_node, partition,
release_variation, type_variation)
ticket_title = "%s has missing allocation supplies." % compute_node.getTitle()
self.assertNotEqual(ticket, None)
self.assertEqual(ticket.getTitle(), ticket_title)
self.tic()
event_list = ticket.getFollowUpRelatedValueList()
self.assertEqual(len(event_list), 1)
event = event_list[0]
self.assertIn("The following contains instances that has Software Releases/Types",
event.getTextContent())
self.assertIn(release_variation.getUrlString(), event.getTextContent())
self.assertIn(type_variation.getReference(), event.getTextContent())
self.assertEventTicket(event, ticket, compute_node)
@simulate('Project_isSupportRequestCreationClosed', '*args, **kwargs', 'return 0')
def test_ComputeNode_checkAllocationConsistencyState_script_InstanceNodenoAllocationCell(self):
with PinnedDateTime(self, DateTime() - 1.1):
_, release_variation, \
type_variation, \
instance_node, \
partition, \
_ = self.bootstrapAllocableInstanceTree(
allocation_state='allocated', node='instance', shared=True)
self.tic()
compute_node = partition.getParentValue()
self.assertEqual(compute_node.getPortalType(), 'Compute Node')
ticket = compute_node.ComputeNode_checkAllocationConsistencyState()
# Double check error dict
error_dict = partition.ComputePartition_checkAllocationConsistencyState()
self.assertNotEqual({}, error_dict)
self.assertEqual(1, len(error_dict))
self.assertEqual([release_variation.getUrlString()], error_dict.keys())
self.assertEqual([type_variation.getReference()],
error_dict[release_variation.getUrlString()].keys())
self.assertEqual(partition.getAggregateRelatedValue().getUid(),
error_dict[release_variation.getUrlString()][type_variation.getReference()][0].getUid())
self.assertEqual(partition.getUid(),
error_dict[release_variation.getUrlString()][type_variation.getReference()][1].getUid())
self.assertEqual(2, len(error_dict), error_dict)
# Type refers to slave instance type
type_reference = type_variation.getReference()
release_url = release_variation.getUrlString()
# Check first missing allocation supply for the instance on the computer
_error_dict = error_dict[compute_node.getRelativeUrl()]
# Try to see if duplicates entries.
error_dict[release_variation.getUrlString()][type_variation.getReference()] = ('a', 'b')
# Pass a known case dont add more entries
self.assertEqual({}, partition.ComputePartition_checkAllocationConsistencyState(
known_error_dict=error_dict))
software_instance = partition.getAggregateRelatedValue(
portal_type="Software Instance")
self.assertEqual([release_url], _error_dict.keys())
instance_software_type = software_instance.getSourceReference()
self.assertEqual([instance_software_type],
_error_dict[release_url].keys())
self.assertEqual(software_instance.getRelativeUrl(),
_error_dict[release_url][instance_software_type][0].getRelativeUrl())
self.assertEqual(compute_node.getRelativeUrl(),
_error_dict[release_url][instance_software_type][1].getRelativeUrl())
for _index in error_dict:
if _index != compute_node.getRelativeUrl():
instance_error_dict = error_dict[_index]
slave_instance = partition.getAggregateRelatedValue(portal_type='Slave Instance')
self.assertEqual([release_url], instance_error_dict.keys())
self.assertEqual([type_reference], instance_error_dict[release_url].keys())
self.assertEqual(slave_instance.getSourceReference(), type_reference)
self.assertEqual(slave_instance.getRelativeUrl(),
instance_error_dict[release_url][type_reference][0].getRelativeUrl())
self.assertNotEqual(compute_node.getRelativeUrl(),
instance_error_dict[release_url][type_reference][1].getRelativeUrl())
instance_node = instance_error_dict[release_url][type_reference][1]
self.assertEqual(instance_node.getPortalType(), 'Instance Node')
ticket_title = "%s has missing allocation supplies." % compute_node.getTitle()
self.assertNotEqual(ticket, None)
......@@ -331,8 +387,9 @@ class TestSlapOSCrmMonitoringCheckAllocationConsistencyState(TestSlapOSCrmMonito
self.assertIn(type_variation.getReference(), event.getTextContent())
self.assertEventTicket(event, ticket, compute_node)
class TestSlapOSCrmMonitoringCheckComputeNodeProjectState(TestSlapOSCrmMonitoringMixin):
launch_caucase = 1
##########################################################################
# slapos_crm_monitoring_project > ComputeNode_checkMonitoringState
......@@ -859,7 +916,6 @@ class TestSlapOSCrmMonitoringCheckComputeNodeState(TestSlapOSCrmMonitoringMixin)
self.assertEventTicket(event, ticket, compute_node)
class TestSlapOSCrmSoftwareInstance_checkInstanceTreeMonitoringState(TestSlapOSCrmMonitoringMixin):
launch_caucase = 1
@simulate('Project_isSupportRequestCreationClosed', '*args, **kwargs', 'return 0')
def test_SoftwareInstance_checkInstanceTreeMonitoringState_alarm_not_called(self):
......
......@@ -37,62 +37,10 @@ if software_product is None:
return
assert software_release.getUrlString() == instance_tree.getUrlString()
compute_node = None
root_instance = None
root_instance_list = [
q for q in instance_tree.getSuccessorValueList(portal_type=["Software Instance", "Slave Instance"])
if q.getSlapState() != 'destroy_requested']
if len(root_instance_list) == 1:
root_instance = root_instance_list[0]
partition = root_instance.getAggregateValue()
if partition is not None:
compute_node = partition.getParentValue()
if (root_instance.getPortalType() == 'Slave Instance'):
if (compute_node.getPortalType() == 'Compute Node'):
# Search the instance node linked to this partition
soft_instance = partition.getAggregateRelatedValue(portal_type='Software Instance')
if soft_instance is None:
return
instance_node = soft_instance.getSpecialiseRelatedValue(portal_type='Instance Node')
if instance_node is None:
return
compute_node = instance_node
elif (compute_node.getPortalType() != 'Remote Node'):
return
person = context.getDestinationSectionValue(checked_permission='Access contents information')
# Search if the product with the same type
# XXX search only for the main node
allocation_cell_list = software_product.getFollowUpValue().Project_getSoftwareProductPredicateList(
compute_node, allocation_cell_list = instance_tree.InstanceTree_getNodeAndAllocationSupplyCellList(
software_product=software_product,
software_product_type=target_software_type or software_type,
software_product_release=target_software_release,
destination_value=person,
node_value=compute_node,
predicate_portal_type='Allocation Supply Cell'
)
if (compute_node is None) and (root_instance is not None):
# Do not upgrade if there is no instance yet
allocation_cell_node_list = [(x, [y.getPortalType() for y in x.getParentValue().getParentValue().getAggregateValueList()]) for x in allocation_cell_list]
if (root_instance.getPortalType() == 'Slave Instance'):
allocation_cell_list = [x for x, y in allocation_cell_node_list if ("Remote Node" in y) or ("Instance Node" in y)]
elif (root_instance.getPortalType() == 'Software Instance'):
allocation_cell_list = [x for x, y in allocation_cell_node_list if ("Remote Node" in y) or ("Compute Node" in y)]
# Remove duplicated allocation cells
# ie, multiple allocation cells matching the same release/type/node
software_release_uid_dict = {}
not_duplicated_allocation_cell_list = []
for allocation_cell in allocation_cell_list:
# Do not return duplicated release values
software_release_uid = allocation_cell.getSoftwareReleaseValue().getUid()
if software_release_uid not in software_release_uid_dict:
software_release_uid_dict[software_release_uid] = None
not_duplicated_allocation_cell_list.append(allocation_cell)
allocation_cell_list = not_duplicated_allocation_cell_list
software_release=target_software_release,
software_type=target_software_type or software_type)
# Only upgrade if there is no doubt (ie, only 1 url is allowed)
if len(allocation_cell_list) == 1:
......
......@@ -80,6 +80,20 @@ class TestSlapOSPDMCreateUpgradeDecisionSkins(SlapOSTestCaseMixin):
instance_tree, software_product, new_release_variation, type_variation
)
# Ensure we are not upgrading for the same thing.
_, allocation_cell_list = instance_tree.InstanceTree_getNodeAndAllocationSupplyCellList(
software_product, new_release_variation, type_variation)
self.assertEqual(len(allocation_cell_list), 1)
self.assertNotEqual(
allocation_cell_list[0].getSoftwareReleaseValue().getUrlString(),
instance_tree.getUrlString())
self.assertEqual(
allocation_cell_list[0].getSoftwareReleaseValue().getUrlString(),
new_release_variation.getUrlString())
def test_createUpgradeDecision_notAllocated_newReleaseOnRemoteNode(self):
software_product, _, type_variation, compute_node, _, instance_tree = self.bootstrapAllocableInstanceTree(node="remote")
self.tic()
......@@ -94,6 +108,19 @@ class TestSlapOSPDMCreateUpgradeDecisionSkins(SlapOSTestCaseMixin):
instance_tree, software_product, new_release_variation, type_variation
)
# Ensure we are not upgrading for the same thing.
_, allocation_cell_list = instance_tree.InstanceTree_getNodeAndAllocationSupplyCellList(
software_product, new_release_variation, type_variation)
self.assertEqual(len(allocation_cell_list), 1)
self.assertNotEqual(
allocation_cell_list[0].getSoftwareReleaseValue().getUrlString(),
instance_tree.getUrlString())
self.assertEqual(
allocation_cell_list[0].getSoftwareReleaseValue().getUrlString(),
new_release_variation.getUrlString())
def test_createUpgradeDecision_notAllocated_newReleaseOnInstanceNode(self):
software_product, _, type_variation, compute_node, _, instance_tree = self.bootstrapAllocableInstanceTree(allocation_state="impossible", node="instance")
self.tic()
......@@ -121,6 +148,19 @@ class TestSlapOSPDMCreateUpgradeDecisionSkins(SlapOSTestCaseMixin):
instance_tree, software_product, new_release_variation, type_variation
)
# Ensure we are not upgrading for the same thing.
_, allocation_cell_list = instance_tree.InstanceTree_getNodeAndAllocationSupplyCellList(
software_product, new_release_variation, type_variation)
self.assertEqual(len(allocation_cell_list), 1)
self.assertNotEqual(
allocation_cell_list[0].getSoftwareReleaseValue().getUrlString(),
instance_tree.getUrlString())
self.assertEqual(
allocation_cell_list[0].getSoftwareReleaseValue().getUrlString(),
new_release_variation.getUrlString())
def test_createUpgradeDecision_notAllocated_sameRelease(self):
software_product, release_variation, type_variation, compute_node, _, instance_tree = self.bootstrapAllocableInstanceTree()
self.tic()
......
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