Commit b3b6e300 authored by Romain Courteaud's avatar Romain Courteaud

slapos_slap_tool: support for the virtual master logic

A project reference is required to create a compute node.

Add compatibility with the user request
Console client does not send a project reference.
Try to guess it for simple cases
parent e7f744c8
...@@ -16,7 +16,9 @@ def getComputeNodeReferenceAndUserId(item): ...@@ -16,7 +16,9 @@ def getComputeNodeReferenceAndUserId(item):
if partition is not None: if partition is not None:
compute_node = partition.getParentValue() compute_node = partition.getParentValue()
if compute_node is not None and compute_node.getValidationState() == 'validated': if (compute_node is not None) and \
(compute_node.getPortalType() == 'Compute Node') and \
(compute_node.getValidationState() == 'validated'):
return compute_node, compute_node.getReference(), compute_node.getUserId() return compute_node, compute_node.getReference(), compute_node.getUserId()
return None, None, None return None, None, None
......
...@@ -6,12 +6,6 @@ ...@@ -6,12 +6,6 @@
</pickle> </pickle>
<pickle> <pickle>
<dictionary> <dictionary>
<item>
<key> <string>_recorded_property_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item> <item>
<key> <string>default_reference</string> </key> <key> <string>default_reference</string> </key>
<value> <string>SlapOSSlapTool</string> </value> <value> <string>SlapOSSlapTool</string> </value>
...@@ -55,28 +49,13 @@ ...@@ -55,28 +49,13 @@
<item> <item>
<key> <string>workflow_history</string> </key> <key> <string>workflow_history</string> </key>
<value> <value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent> <persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
</record> </record>
<record id="2" aka="AAAAAAAAAAI="> <record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle> <pickle>
<global name="PersistentMapping" module="Persistence.mapping"/> <global name="PersistentMapping" module="Persistence.mapping"/>
</pickle> </pickle>
...@@ -89,7 +68,7 @@ ...@@ -89,7 +68,7 @@
<item> <item>
<key> <string>component_validation_workflow</string> </key> <key> <string>component_validation_workflow</string> </key>
<value> <value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent> <persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value> </value>
</item> </item>
</dictionary> </dictionary>
...@@ -98,7 +77,7 @@ ...@@ -98,7 +77,7 @@
</dictionary> </dictionary>
</pickle> </pickle>
</record> </record>
<record id="4" aka="AAAAAAAAAAQ="> <record id="3" aka="AAAAAAAAAAM=">
<pickle> <pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/> <global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle> </pickle>
......
...@@ -5,8 +5,8 @@ class TestSlapOSCoreComputeNodeUpdateFromDict(SlapOSTestCaseMixinWithAbort): ...@@ -5,8 +5,8 @@ class TestSlapOSCoreComputeNodeUpdateFromDict(SlapOSTestCaseMixinWithAbort):
def afterSetUp(self): def afterSetUp(self):
SlapOSTestCaseMixinWithAbort.afterSetUp(self) SlapOSTestCaseMixinWithAbort.afterSetUp(self)
self.compute_node = self.portal.compute_node_module.template_compute_node\ self.compute_node = self.portal.compute_node_module\
.Base_createCloneDocument(batch_mode=1) .newContent(portal_type="Compute Node")
self.compute_node.edit( self.compute_node.edit(
reference='TESTC-%s' % self.generateNewId(), reference='TESTC-%s' % self.generateNewId(),
) )
......
...@@ -373,7 +373,7 @@ class SlapTool(BaseTool): ...@@ -373,7 +373,7 @@ class SlapTool(BaseTool):
security.declareProtected(Permissions.AccessContentsInformation, security.declareProtected(Permissions.AccessContentsInformation,
'getSoftwareReleaseListFromSoftwareProduct') 'getSoftwareReleaseListFromSoftwareProduct')
def getSoftwareReleaseListFromSoftwareProduct(self, def getSoftwareReleaseListFromSoftwareProduct(self, project_reference,
software_product_reference=None, software_release_url=None): software_product_reference=None, software_release_url=None):
""" """
Get the list of all published Software Releases related to one of either: Get the list of all published Software Releases related to one of either:
...@@ -384,12 +384,23 @@ class SlapTool(BaseTool): ...@@ -384,12 +384,23 @@ class SlapTool(BaseTool):
If referenced Software Product does not exist, return empty list. If referenced Software Product does not exist, return empty list.
If referenced Software Release does not exist, raise. If referenced Software Release does not exist, raise.
""" """
project_list = self.getPortalObject().portal_catalog.portal_catalog(
portal_type='Project',
reference=project_reference,
validation_state='validated',
limit=2
)
if len(project_list) != 1:
raise NotImplementedError("%i projects '%s'" % (len(project_list), project_reference))
project = project_list[0]
if software_product_reference is None: if software_product_reference is None:
assert(software_release_url is not None) assert(software_release_url is not None)
software_product_reference = self.getPortalObject().portal_catalog.unrestrictedSearchResults( software_product_reference = self.getPortalObject().portal_catalog.unrestrictedSearchResults(
portal_type='Software Release', portal_type='Software Product Release Variation',
parent__follow_up__uid=project.getUid(),
url_string=software_release_url url_string=software_release_url
)[0].getObject().getAggregateValue().getReference() )[0].getObject().getParentValue().getReference()
else: else:
# Don't accept both parameters # Don't accept both parameters
assert(software_release_url is None) assert(software_release_url is None)
...@@ -397,13 +408,14 @@ class SlapTool(BaseTool): ...@@ -397,13 +408,14 @@ class SlapTool(BaseTool):
software_product_list = self.getPortalObject().portal_catalog.unrestrictedSearchResults( software_product_list = self.getPortalObject().portal_catalog.unrestrictedSearchResults(
portal_type='Software Product', portal_type='Software Product',
reference=software_product_reference, reference=software_product_reference,
follow_up__uid=project.getUid(),
validation_state='published') validation_state='published')
if len(software_product_list) is 0: if len(software_product_list) is 0:
return dumps([]) return dumps([])
if len(software_product_list) > 1: if len(software_product_list) > 1:
raise NotImplementedError('Several Software Product with the same title.') raise NotImplementedError('Several Software Product with the same title.')
software_release_list = \ software_release_list = \
software_product_list[0].getObject().getAggregateRelatedValueList() software_product_list[0].getObject().contentValues(portal_type='Software Product Release Variation')
def sortkey(software_release): def sortkey(software_release):
publication_date = software_release.getEffectiveDate() publication_date = software_release.getEffectiveDate()
...@@ -420,9 +432,7 @@ class SlapTool(BaseTool): ...@@ -420,9 +432,7 @@ class SlapTool(BaseTool):
) )
return dumps( return dumps(
[software_release.getUrlString() [software_release.getUrlString()
for software_release in software_release_list for software_release in software_release_list])
if software_release.getValidationState() in \
['published', 'published_alive']])
security.declareProtected(Permissions.AccessContentsInformation, security.declareProtected(Permissions.AccessContentsInformation,
'getHateoasUrl') 'getHateoasUrl')
...@@ -508,20 +518,20 @@ class SlapTool(BaseTool): ...@@ -508,20 +518,20 @@ class SlapTool(BaseTool):
return self._supplySupply(url, computer_id, state) return self._supplySupply(url, computer_id, state)
@convertToREST @convertToREST
def _requestComputeNode(self, compute_node_title): def _requestComputeNode(self, compute_node_title, project_reference):
portal = self.getPortalObject() portal = self.getPortalObject()
person = portal.portal_membership.getAuthenticatedMember().getUserValue() person = portal.portal_membership.getAuthenticatedMember().getUserValue()
person.requestComputeNode(compute_node_title=compute_node_title) person.requestComputeNode(compute_node_title=compute_node_title, project_reference=project_reference)
compute_node = ComputeNode(self.REQUEST.get('compute_node_reference').decode("UTF-8")) compute_node = ComputeNode(self.REQUEST.get('compute_node_reference').decode("UTF-8"))
return dumps(compute_node) return dumps(compute_node)
security.declareProtected(Permissions.AccessContentsInformation, security.declareProtected(Permissions.AccessContentsInformation,
'requestComputer') 'requestComputer')
def requestComputer(self, computer_title): def requestComputer(self, computer_title, project_reference):
""" """
Request Compute Node Request Compute Node
""" """
return self._requestComputeNode(computer_title) return self._requestComputeNode(computer_title, project_reference)
security.declareProtected(Permissions.AccessContentsInformation, security.declareProtected(Permissions.AccessContentsInformation,
'buildingSoftwareRelease') 'buildingSoftwareRelease')
...@@ -615,7 +625,7 @@ class SlapTool(BaseTool): ...@@ -615,7 +625,7 @@ class SlapTool(BaseTool):
def requestComputerPartition(self, computer_id=None, def requestComputerPartition(self, computer_id=None,
computer_partition_id=None, software_release=None, software_type=None, computer_partition_id=None, software_release=None, software_type=None,
partition_reference=None, partition_parameter_xml=None, partition_reference=None, partition_parameter_xml=None,
filter_xml=None, state=None, shared_xml=_MARKER): filter_xml=None, state=None, shared_xml=_MARKER, project_reference=None):
""" """
Asynchronously requests creation of compute partition for assigned Asynchronously requests creation of compute partition for assigned
parameters parameters
...@@ -629,7 +639,8 @@ class SlapTool(BaseTool): ...@@ -629,7 +639,8 @@ class SlapTool(BaseTool):
""" """
return self._requestComputePartition(computer_id, computer_partition_id, return self._requestComputePartition(computer_id, computer_partition_id,
software_release, software_type, partition_reference, software_release, software_type, partition_reference,
shared_xml, partition_parameter_xml, filter_xml, state) shared_xml, partition_parameter_xml, filter_xml, state,
project_reference)
security.declareProtected(Permissions.AccessContentsInformation, security.declareProtected(Permissions.AccessContentsInformation,
'useComputer') 'useComputer')
...@@ -926,7 +937,8 @@ class SlapTool(BaseTool): ...@@ -926,7 +937,8 @@ class SlapTool(BaseTool):
@convertToREST @convertToREST
def _requestComputePartition(self, compute_node_id, compute_partition_id, def _requestComputePartition(self, compute_node_id, compute_partition_id,
software_release, software_type, partition_reference, software_release, software_type, partition_reference,
shared_xml, partition_parameter_xml, filter_xml, state): shared_xml, partition_parameter_xml, filter_xml, state,
project_reference):
""" """
Asynchronously requests creation of compute partition for assigned Asynchronously requests creation of compute partition for assigned
parameters parameters
...@@ -964,7 +976,8 @@ class SlapTool(BaseTool): ...@@ -964,7 +976,8 @@ class SlapTool(BaseTool):
instance_xml=castToStr(partition_parameter_kw), instance_xml=castToStr(partition_parameter_kw),
shared=shared, shared=shared,
sla_xml=castToStr(filter_kw), sla_xml=castToStr(filter_kw),
state=state) state=state,
project_reference=project_reference)
portal = self.getPortalObject() portal = self.getPortalObject()
if compute_node_id and compute_partition_id: if compute_node_id and compute_partition_id:
...@@ -978,8 +991,48 @@ class SlapTool(BaseTool): ...@@ -978,8 +991,48 @@ class SlapTool(BaseTool):
else: else:
# requested as root, so done by human # requested as root, so done by human
requester = portal.portal_membership.getAuthenticatedMember().getUserValue() requester = portal.portal_membership.getAuthenticatedMember().getUserValue()
if project_reference is None:
# Compatibility with the slapos console client
# which does not send any project_reference parameter
# and always connect to a uniq url
# Try to guess the project reference automatically
project_list = portal.portal_catalog(portal_type='Project', limit=2)
if len(project_list) == 1:
# If the user only has access to one project
# we can easily suppose the request must be allocated here
kw['project_reference'] = project_list[0].getReference()
else:
release_variation_list = portal.portal_catalog(
portal_type='Software Product Release Variation',
url_string=software_release,
limit=2
)
if len(release_variation_list) == 1:
# If the user only has access to matching release variation
# we can easily suppose the request must be allocated on the same project
kw['project_reference'] = release_variation_list[0].getParentValue().getFollowUpReference()
# Finally, try to use the SLA parameter to guess where it could be
elif 'project_guid' in filter_kw:
kw['project_reference'] = filter_kw['project_guid']
elif 'computer_guid' in filter_kw:
computer_list = portal.portal_catalog(
portal_type=['Compute Node', 'Remote Node', 'Instance Node'],
limit=2
)
if len(computer_list == 1):
kw['project_reference'] = computer_list[0].getFollowUpReference()
elif 'network_guid' in filter_kw:
network_list = portal.portal_catalog(
portal_type='Computer Network',
limit=2
)
if len(network_list == 1):
kw['project_reference'] = network_list[0].getFollowUpReference()
key = '_'.join([requester.getRelativeUrl(), partition_reference]) key = '_'.join([requester.getRelativeUrl(), partition_reference])
last_data = requester.getLastData(key) last_data = requester.getLastData(key)
requested_software_instance = None requested_software_instance = None
value = dict( value = dict(
...@@ -989,7 +1042,7 @@ class SlapTool(BaseTool): ...@@ -989,7 +1042,7 @@ class SlapTool(BaseTool):
if last_data is not None and isinstance(last_data, type(value)): if last_data is not None and isinstance(last_data, type(value)):
requested_software_instance = self.restrictedTraverse( requested_software_instance = self.restrictedTraverse(
last_data.get('request_instance'), None) last_data.get('request_instance'), None)
if last_data is None or not isinstance(last_data, type(value)) or \ if last_data is None or not isinstance(last_data, type(value)) or \
last_data.get('hash') != value['hash'] or \ last_data.get('hash') != value['hash'] or \
requested_software_instance is None: requested_software_instance is None:
......
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