Commit 0dbf8e50 authored by Rafael Monnerat's avatar Rafael Monnerat

slapos_cloud: Extend catalog API for specific Compute [Note/Partition] search

    This API extends catalog tool to search node and partitions using unrestricted search and accertACI.

    The mains goal here are:
       - raise Unauthorized (instead NotFound) when the object exists but you cannot have access
       - Globaly cache when you find a computer since this is quite intense operation
       - Be able to search regardless security (which is required for some usecases).
parent 6623f7e3
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2010-2022 Nexedi SA and Contributors. All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly advised to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
from erp5.component.module.SlapOSCloud import _assertACI
from OFS.Traversable import NotFound
from Products.ERP5Type.Cache import CachingMethod
from Products.ERP5Type.UnrestrictedMethod import UnrestrictedMethod
class SlapOSCatalogToolCacheMixin(object):
""" Quering Caching Extension for Catalog for handle specific queries
relying on caching.
The searches also differ NotFound from Unauthorized, by relying into
unrestricted searches and a custom way to assert Access roles.
Be carefull to not rely on it to hack arround security.
"""
def _getNonCachedComputeNode(self, reference):
# No need to get all results if an error is raised when at least 2 objects
# are found
compute_node_list = self.unrestrictedSearchResults(limit=2,
portal_type='Compute Node',
validation_state="validated",
reference=reference)
if len(compute_node_list) != 1:
raise NotFound, "No document found with parameters: %s" % reference
else:
return _assertACI(compute_node_list[0].getObject()).getRelativeUrl()
def getComputeNodeObject(self, reference):
"""
Get the validated compute_node with this reference.
"""
result = CachingMethod(self._getNonCachedComputeNode,
id='_getComputeNodeObject',
cache_factory='slap_cache_factory')(reference)
return self.getPortalObject().restrictedTraverse(result)
@UnrestrictedMethod
def _getComputeNodeUid(self, reference):
"""
Get the validated compute_node with this reference.
"""
return CachingMethod(self._getNonCachedComputeNodeUid,
id='_getNonCachedComputeNodeUid',
cache_factory='slap_cache_factory')(reference)
@UnrestrictedMethod
def _getNonCachedComputeNodeUid(self, reference):
return self.unrestrictedSearchResults(
portal_type='Compute Node', reference=reference,
validation_state="validated")[0].UID
def getComputePartitionObject(self, compute_node_reference,
compute_partition_reference):
"""
Get the compute partition defined in an available compute_node
"""
# Related key might be nice
compute_partition_list = self.unrestrictedSearchResults(limit=2,
portal_type='Compute Partition',
reference=compute_partition_reference,
parent_uid=self._getNonCachedComputeNodeUid(
compute_node_reference))
if len(compute_partition_list) != 1:
raise NotFound, "No document found with parameters: %s %s" % \
(compute_node_reference, compute_partition_reference)
else:
return _assertACI(compute_partition_list[0].getObject())
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Mixin Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>default_reference</string> </key>
<value> <string>SlapOSCatalogToolCacheMixin</string> </value>
</item>
<item>
<key> <string>default_source_reference</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>mixin.erp5.SlapOSCatalogToolCacheMixin</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Mixin Component</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<type_mixin> <type_mixin>
<portal_type id="Catalog Tool">
<item>SlapOSCatalogToolCacheMixin</item>
</portal_type>
<portal_type id="Compute Node"> <portal_type id="Compute Node">
<item>SlapOSCacheMixin</item> <item>SlapOSCacheMixin</item>
<item>SlapOSComputeNodeMixin</item> <item>SlapOSComputeNodeMixin</item>
......
mixin.erp5.SlapOSCacheMixin mixin.erp5.SlapOSCacheMixin
mixin.erp5.SlapOSComputeNodeMixin mixin.erp5.SlapOSComputeNodeMixin
mixin.erp5.SlapOSComputePartitionMixin mixin.erp5.SlapOSComputePartitionMixin
\ No newline at end of file mixin.erp5.SlapOSCatalogToolCacheMixin
\ No newline at end of file
Catalog Tool | SlapOSCatalogToolCacheMixin
Compute Node | SlapOSCacheMixin Compute Node | SlapOSCacheMixin
Compute Node | SlapOSComputeNodeMixin Compute Node | SlapOSComputeNodeMixin
Compute Partition | SlapOSCacheMixin Compute Partition | SlapOSCacheMixin
......
...@@ -321,7 +321,7 @@ class SlapTool(BaseTool): ...@@ -321,7 +321,7 @@ class SlapTool(BaseTool):
""" """
Get the connection status of the partition Get the connection status of the partition
""" """
compute_node = self._getComputeNodeDocument(computer_id) compute_node = self.portal_catalog.getComputeNodeObject(computer_id)
data_dict = compute_node.getAccessStatus() data_dict = compute_node.getAccessStatus()
# Keep in cache server for 7 days # Keep in cache server for 7 days
...@@ -341,7 +341,7 @@ class SlapTool(BaseTool): ...@@ -341,7 +341,7 @@ class SlapTool(BaseTool):
""" """
Get the connection status of the software installation Get the connection status of the software installation
""" """
compute_node = self._getComputeNodeDocument(computer_id) compute_node = self.portal_catalog.getComputeNodeObject(computer_id)
# Be sure to prevent accessing information to disallowed users # Be sure to prevent accessing information to disallowed users
compute_node = _assertACI(compute_node) compute_node = _assertACI(compute_node)
try: try:
...@@ -620,7 +620,7 @@ class SlapTool(BaseTool): ...@@ -620,7 +620,7 @@ class SlapTool(BaseTool):
'doc/computer_consumption.xsd') 'doc/computer_consumption.xsd')
if self._validateXML(use_string, compute_node_consumption_model): if self._validateXML(use_string, compute_node_consumption_model):
compute_node = self._getComputeNodeDocument(computer_id) compute_node = self.portal_catalog.getComputeNodeObject(computer_id)
tree = etree.fromstring(use_string) tree = etree.fromstring(use_string)
source_reference = \ source_reference = \
tree.find('transaction').find('reference').text or "" tree.find('transaction').find('reference').text or ""
...@@ -641,7 +641,7 @@ class SlapTool(BaseTool): ...@@ -641,7 +641,7 @@ class SlapTool(BaseTool):
""" """
Fire up bung on Compute Node Fire up bung on Compute Node
""" """
compute_node = self._getComputeNodeDocument(compute_node_id) compute_node = self.getPortalObject().portal_catalog.getComputeNodeObject(compute_node_id)
return compute_node.reportComputeNodeBang(comment=message) return compute_node.reportComputeNodeBang(comment=message)
security.declareProtected(Permissions.AccessContentsInformation, security.declareProtected(Permissions.AccessContentsInformation,
...@@ -657,13 +657,13 @@ class SlapTool(BaseTool): ...@@ -657,13 +657,13 @@ class SlapTool(BaseTool):
def loadComputerConfigurationFromXML(self, xml): def loadComputerConfigurationFromXML(self, xml):
"Load the given xml as configuration for the compute_node object" "Load the given xml as configuration for the compute_node object"
compute_node_dict = loads(xml) compute_node_dict = loads(xml)
compute_node = self._getComputeNodeDocument(compute_node_dict['reference']) compute_node = self.getPortalObject().portal_catalog.getComputeNodeObject(compute_node_dict['reference'])
compute_node.ComputeNode_updateFromDict(compute_node_dict) compute_node.ComputeNode_updateFromDict(compute_node_dict)
return 'Content properly posted.' return 'Content properly posted.'
@convertToREST @convertToREST
def _generateComputerCertificate(self, compute_node_id): def _generateComputerCertificate(self, compute_node_id):
self._getComputeNodeDocument(compute_node_id).generateCertificate() self.getPortalObject().portal_catalog.getComputeNodeObject(compute_node_id).generateCertificate()
result = { result = {
'certificate': self.REQUEST.get('compute_node_certificate').decode("UTF-8"), 'certificate': self.REQUEST.get('compute_node_certificate').decode("UTF-8"),
'key': self.REQUEST.get('compute_node_key').decode("UTF-8") 'key': self.REQUEST.get('compute_node_key').decode("UTF-8")
...@@ -678,7 +678,7 @@ class SlapTool(BaseTool): ...@@ -678,7 +678,7 @@ class SlapTool(BaseTool):
@convertToREST @convertToREST
def _revokeComputerCertificate(self, compute_node_id): def _revokeComputerCertificate(self, compute_node_id):
self._getComputeNodeDocument(compute_node_id).revokeCertificate() self.getPortalObject().portal_catalog.getComputeNodeObject(compute_node_id).revokeCertificate()
security.declareProtected(Permissions.AccessContentsInformation, security.declareProtected(Permissions.AccessContentsInformation,
'revokeComputerCertificate') 'revokeComputerCertificate')
...@@ -696,7 +696,7 @@ class SlapTool(BaseTool): ...@@ -696,7 +696,7 @@ class SlapTool(BaseTool):
""" """
# Try to get the compute partition to raise an exception if it doesn't # Try to get the compute partition to raise an exception if it doesn't
# exist # exist
compute_partition_document = self._getComputePartitionDocument( compute_partition_document = self.getPortalObject().portal_catalog.getComputePartitionObject(
computer_reference, computer_partition_reference) computer_reference, computer_partition_reference)
partition_dict = compute_partition_document._registerComputePartition() partition_dict = compute_partition_document._registerComputePartition()
...@@ -762,7 +762,7 @@ class SlapTool(BaseTool): ...@@ -762,7 +762,7 @@ class SlapTool(BaseTool):
""" """
Request Software Release installation Request Software Release installation
""" """
compute_node_document = self._getComputeNodeDocument(compute_node_id) compute_node_document = self.getPortalObject().portal_catalog.getComputeNodeObject(compute_node_id)
compute_node_document.requestSoftwareRelease(software_release_url=url, state=state) compute_node_document.requestSoftwareRelease(software_release_url=url, state=state)
@convertToREST @convertToREST
...@@ -770,7 +770,7 @@ class SlapTool(BaseTool): ...@@ -770,7 +770,7 @@ class SlapTool(BaseTool):
""" """
Log the software release status Log the software release status
""" """
compute_node = self._getComputeNodeDocument(compute_node_id) compute_node = self.getPortalObject().portal_catalog.getComputeNodeObject(compute_node_id)
software_installation = compute_node._getSoftwareInstallationFromUrl(url) software_installation = compute_node._getSoftwareInstallationFromUrl(url)
software_installation.setBuildingStatus( software_installation.setBuildingStatus(
'software release %s' % url, "building") 'software release %s' % url, "building")
...@@ -780,7 +780,7 @@ class SlapTool(BaseTool): ...@@ -780,7 +780,7 @@ class SlapTool(BaseTool):
""" """
Log the software release status Log the software release status
""" """
compute_node = self._getComputeNodeDocument(compute_node_id) compute_node = self.getPortalObject().portal_catalog.getComputeNodeObject(compute_node_id)
software_installation = compute_node._getSoftwareInstallationFromUrl(url) software_installation = compute_node._getSoftwareInstallationFromUrl(url)
software_installation.setAccessStatus( software_installation.setAccessStatus(
'software release %s available' % url, "available") 'software release %s available' % url, "available")
...@@ -790,7 +790,7 @@ class SlapTool(BaseTool): ...@@ -790,7 +790,7 @@ class SlapTool(BaseTool):
""" """
Reports that Software Release is destroyed Reports that Software Release is destroyed
""" """
compute_node = self._getComputeNodeDocument(compute_node_id) compute_node = self.getPortalObject().portal_catalog.getComputeNodeObject(compute_node_id)
software_installation = compute_node._getSoftwareInstallationFromUrl(url) software_installation = compute_node._getSoftwareInstallationFromUrl(url)
if software_installation.getSlapState() != 'destroy_requested': if software_installation.getSlapState() != 'destroy_requested':
raise NotFound raise NotFound
...@@ -1012,61 +1012,9 @@ class SlapTool(BaseTool): ...@@ -1012,61 +1012,9 @@ class SlapTool(BaseTool):
# Internals methods # Internals methods
#################################################### ####################################################
def _getDocument(self, **kwargs):
# No need to get all results if an error is raised when at least 2 objects
# are found
l = self.getPortalObject().portal_catalog.unrestrictedSearchResults(limit=2, **kwargs)
if len(l) != 1:
raise NotFound, "No document found with parameters: %s" % kwargs
else:
return _assertACI(l[0].getObject())
def _getNonCachedComputeNodeDocument(self, compute_node_reference):
return self._getDocument(
portal_type='Compute Node',
# XXX Hardcoded validation state
validation_state="validated",
reference=compute_node_reference).getRelativeUrl()
def _getComputeNodeDocument(self, compute_node_reference):
"""
Get the validated compute_node with this reference.
"""
result = CachingMethod(self._getNonCachedComputeNodeDocument,
id='_getComputeNodeDocument',
cache_factory='slap_cache_factory')(compute_node_reference)
return self.getPortalObject().restrictedTraverse(result)
@UnrestrictedMethod
def _getComputeNodeUidByReference(self, compute_node_reference):
"""
Get the validated compute_node with this reference.
"""
result = CachingMethod(self._getNonCachedComputeNodeUidByReference,
id='_getNonCachedComputeNodeUidByReference',
cache_factory='slap_cache_factory')(compute_node_reference)
return result
@UnrestrictedMethod
def _getNonCachedComputeNodeUidByReference(self, compute_node_reference):
return self.getPortalObject().portal_catalog.unrestrictedSearchResults(
portal_type='Compute Node', reference=compute_node_reference,
validation_state="validated")[0].UID
def _getComputePartitionDocument(self, compute_node_reference,
compute_partition_reference):
"""
Get the compute partition defined in an available compute_node
"""
# Related key might be nice
return self._getDocument(portal_type='Compute Partition',
reference=compute_partition_reference,
parent_uid=self._getComputeNodeUidByReference(
compute_node_reference))
def _getSoftwareInstanceForComputePartition(self, compute_node_id, def _getSoftwareInstanceForComputePartition(self, compute_node_id,
compute_partition_id, slave_reference=None): compute_partition_id, slave_reference=None):
compute_partition_document = self._getComputePartitionDocument( compute_partition_document = self.getPortalObject().portal_catalog.getComputePartitionObject(
compute_node_id, compute_partition_id) compute_node_id, compute_partition_id)
return compute_partition_document._getSoftwareInstance(slave_reference) return compute_partition_document._getSoftwareInstance(slave_reference)
...@@ -1075,7 +1023,7 @@ class SlapTool(BaseTool): ...@@ -1075,7 +1023,7 @@ class SlapTool(BaseTool):
""" """
Log the compute_node status Log the compute_node status
""" """
compute_node = self._getComputeNodeDocument(compute_node_id) compute_node = self.getPortalObject().portal_catalog.getComputeNodeObject(compute_node_id)
software_installation = compute_node._getSoftwareInstallationFromUrl(url) software_installation = compute_node._getSoftwareInstallationFromUrl(url)
software_installation.setErrorStatus('while installing %s' % url) software_installation.setErrorStatus('while installing %s' % url)
......
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