Commit 9998c6f4 authored by Rafael Monnerat's avatar Rafael Monnerat

Clean up for the JSON API work

See merge request !388
parents 1ac3ec59 e36b68d4
......@@ -31,31 +31,18 @@ from erp5.component.document.Item import Item
from lxml import etree
import collections
from AccessControl import Unauthorized
from AccessControl.Permissions import access_contents_information
from AccessControl import getSecurityManager
from Products.ERP5Type.UnrestrictedMethod import UnrestrictedMethod
from erp5.component.module.SlapOSCloud import _assertACI
from zLOG import LOG, INFO
try:
from slapos.slap.slap import \
SoftwareInstance as SlapSoftwareInstance
from slapos.util import xml2dict, loads
except ImportError:
def xml2dict(dictionary):
raise ImportError
def loads(*args):
raise ImportError
class SlapSoftwareInstance:
def __init__(self):
raise ImportError
def _assertACI(document):
sm = getSecurityManager()
if sm.checkPermission(access_contents_information,
document):
return document
raise Unauthorized('User %r has no access to %r' % (sm.getUser(), document))
class DisconnectedSoftwareTree(Exception):
pass
......@@ -160,7 +147,7 @@ class SoftwareInstance(Item):
LOG('SoftwareInstance', INFO, 'Issue during parsing xml:', error=True)
return result_dict
def _asSoftwareInstance(self):
def _asSoftwareInstanceDict(self):
parameter_dict = self._asParameterDict()
requested_state = self.getSlapState()
......@@ -182,13 +169,13 @@ class SoftwareInstance(Item):
parameter_dict.pop('filter_xml'))
instance_guid = parameter_dict.pop('instance_guid')
software_instance = SlapSoftwareInstance(**parameter_dict)
software_instance._parameter_dict = xml
software_instance._connection_dict = connection_xml
software_instance._filter_dict = filter_xml
software_instance._requested_state = state
software_instance._instance_guid = instance_guid
return software_instance
software_instance_dict = parameter_dict
software_instance_dict['_parameter_dict'] = xml
software_instance_dict['_connection_dict'] = connection_xml
software_instance_dict['_filter_dict'] = filter_xml
software_instance_dict['_requested_state'] = state
software_instance_dict['_instance_guid'] = instance_guid
return software_instance_dict
@UnrestrictedMethod
def _asParameterDict(self, shared_instance_sql_list=None):
......
......@@ -6,12 +6,6 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_recorded_property_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>SoftwareInstance</string> </value>
......@@ -55,28 +49,13 @@
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
<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/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
......@@ -89,7 +68,7 @@
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
......@@ -98,7 +77,7 @@
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
......
# -*- 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>
......@@ -28,46 +28,22 @@
from Products.ERP5Type.Cache import DEFAULT_CACHE_SCOPE
from AccessControl import Unauthorized
from AccessControl.Permissions import access_contents_information
from AccessControl import getSecurityManager
from Products.ERP5Type.UnrestrictedMethod import UnrestrictedMethod
from Products.ERP5Type.tests.utils import DummyMailHostMixin
from OFS.Traversable import NotFound
from erp5.component.module.SlapOSCloud import _assertACI
import time
from lxml import etree
from zLOG import LOG, INFO
try:
from slapos.slap.slap import (
Computer as ComputeNode,
ComputerPartition as SlapComputePartition,
SoftwareRelease)
from slapos.util import xml2dict, dumps
from slapos.util import xml2dict
except ImportError:
# Do no prevent instance from starting
# if libs are not installed
class ComputeNode:
def __init__(self):
raise ImportError
class SlapComputePartition:
def __init__(self):
raise ImportError
class SoftwareRelease:
def __init__(self):
raise ImportError
def xml2dict(dictionary):
raise ImportError
def dumps(*args):
raise ImportError
def _assertACI(document):
sm = getSecurityManager()
if sm.checkPermission(access_contents_information,
document):
return document
raise Unauthorized('User %r has no access to %r' % (sm.getUser(), document))
class SlapOSComputeNodeMixin(object):
......@@ -87,42 +63,42 @@ class SlapOSComputeNodeMixin(object):
validation_state='validated',
):
software_installation = _assertACI(software_installation.getObject())
software_release_response = SoftwareRelease(
software_release=software_installation.getUrlString().decode('UTF-8'),
computer_guid=self.getReference().decode('UTF-8'))
software_release_dict = {
"software_release": software_installation.getUrlString().decode('UTF-8'),
"computer_guid": self.getReference().decode('UTF-8')
}
if software_installation.getSlapState() == 'destroy_requested':
software_release_response._requested_state = 'destroyed'
software_release_dict["_requested_state"] = 'destroyed'
else:
software_release_response._requested_state = 'available'
software_release_dict["_requested_state"] = 'available'
known_state = software_installation.getTextAccessStatus()
if known_state.startswith("#access"):
software_release_response._known_state = 'available'
software_release_dict["_known_state"] = 'available'
elif known_state.startswith("#building"):
software_release_response._known_state = 'building'
software_release_dict["_known_state"] = 'building'
else:
software_release_response._known_state = 'error'
software_release_dict["_known_state"] = 'error'
software_release_list.append(software_release_response)
software_release_list.append(software_release_dict)
return software_release_list
def _getCacheComputeNodeInformation(self, user):
self.REQUEST.response.setHeader('Content-Type', 'text/xml; charset=utf-8')
slap_compute_node = ComputeNode(self.getReference().decode("UTF-8"))
slap_compute_node._computer_partition_list = []
slap_compute_node._software_release_list = self._getSoftwareReleaseValueList()
compute_node_dict = {
"_computer_id": self.getReference().decode("UTF-8"),
"_computer_partition_list": [],
"_software_release_list": self._getSoftwareReleaseValueList()
}
unrestrictedSearchResults = self.getPortalObject().portal_catalog.unrestrictedSearchResults
compute_partition_list = unrestrictedSearchResults(
parent_uid=self.getUid(),
validation_state="validated",
portal_type="Compute Partition"
)
self._calculateSlapComputeNodeInformation(slap_compute_node, compute_partition_list)
self._calculateSlapComputeNodeInformation(compute_node_dict, compute_partition_list)
return dumps(slap_compute_node)
return compute_node_dict
def _activateFillComputeNodeInformationCache(self, user):
tag = 'compute_node_information_cache_fill_%s_%s' % (self.getReference(), user)
......@@ -133,11 +109,14 @@ class SlapOSComputeNodeMixin(object):
def _fillComputeNodeInformationCache(self, user):
key = '%s_%s' % (self.getReference(), user)
try:
computer_dict = self._getCacheComputeNodeInformation(user)
self._getCachePlugin().set(key, DEFAULT_CACHE_SCOPE,
dict (
time=time.time(),
refresh_etag=self._calculateRefreshEtag(),
data=self._getCacheComputeNodeInformation(user),
data=computer_dict,
# Store the XML while SlapTool Still used
data_xml=self.getPortalObject().portal_slap._getSlapComputeNodeXMLFromDict(computer_dict)
),
cache_duration=self.getPortalObject().portal_caches\
.getRamCacheRoot().get('compute_node_information_cache_factory'\
......@@ -178,11 +157,7 @@ class SlapOSComputeNodeMixin(object):
user_document = _assertACI(portal.portal_catalog.unrestrictedGetResultValue(
reference=user, portal_type=['Person', 'Compute Node', 'Software Instance']))
user_type = user_document.getPortalType()
self.REQUEST.response.setHeader('Content-Type', 'text/xml; charset=utf-8')
slap_compute_node = ComputeNode(self.getReference().decode("UTF-8"))
slap_compute_node._computer_partition_list = []
if user_type in ('Compute Node', 'Person'):
if not self._isTestRun():
cache_plugin = self._getCachePlugin()
......@@ -207,22 +182,27 @@ class SlapOSComputeNodeMixin(object):
else:
return self._getCacheComputeNodeInformation(user), None
else:
slap_compute_node._software_release_list = []
compute_node_dict = {
"_computer_id": self.getReference().decode("UTF-8"),
"_computer_partition_list": [],
"_software_release_list": []
}
if user_type == 'Software Instance':
compute_partition_list = self.contentValues(
portal_type="Compute Partition",
checked_permission="View")
else:
compute_partition_list = self.getPortalObject().portal_catalog.unrestrictedSearchResults(
unrestrictedSearchResults = self.getPortalObject().portal_catalog.unrestrictedSearchResults
compute_partition_list = unrestrictedSearchResults(
parent_uid=self.getUid(),
validation_state="validated",
portal_type="Compute Partition")
self._calculateSlapComputeNodeInformation(slap_compute_node, compute_partition_list)
return dumps(slap_compute_node), None
self._calculateSlapComputeNodeInformation(compute_node_dict, compute_partition_list)
return compute_node_dict, None
def _calculateSlapComputeNodeInformation(self, slap_compute_node, compute_partition_list):
def _calculateSlapComputeNodeInformation(self, compute_node_dict, compute_partition_list):
if len(compute_partition_list) == 0:
return
......@@ -248,7 +228,7 @@ class SlapOSComputeNodeMixin(object):
software_instance_list = [x for x in grouped_software_instance_list if (x.default_aggregate_uid == compute_partition.getUid())]
if (len(software_instance_list) == 1) and (software_instance_list[0]['count(*)'] > 1):
software_instance_list = software_instance_list + software_instance_list
slap_compute_node._computer_partition_list.append(
compute_node_dict['_computer_partition_list'].append(
self._getSlapPartitionByPackingList(
_assertACI(compute_partition.getObject()),
software_instance_list,
......@@ -273,12 +253,13 @@ class SlapOSComputeNodeMixin(object):
while compute_node.getPortalType() != 'Compute Node':
compute_node = compute_node.getParentValue()
compute_node_id = compute_node.getReference().decode("UTF-8")
slap_partition = SlapComputePartition(compute_node_id,
compute_partition_document.getReference().decode("UTF-8"))
slap_partition._software_release_document = None
slap_partition._requested_state = 'destroyed'
slap_partition._need_modification = 0
partition_dict = {
"compute_node_id": compute_node_id,
"partition_id": compute_partition_document.getReference().decode("UTF-8"),
"_software_release_document": None,
"_requested_state": 'destroyed',
"_need_modification": 0
}
software_instance = None
......@@ -294,28 +275,29 @@ class SlapOSComputeNodeMixin(object):
if software_instance is not None:
state = software_instance.getSlapState()
if state == "stop_requested":
slap_partition._requested_state = 'stopped'
partition_dict['_requested_state'] = 'stopped'
if state == "start_requested":
slap_partition._requested_state = 'started'
slap_partition._access_status = software_instance.getTextAccessStatus()
partition_dict['_requested_state'] = 'started'
partition_dict['_access_status'] = software_instance.getTextAccessStatus()
slap_partition._software_release_document = SoftwareRelease(
software_release=software_instance.getUrlString().decode("UTF-8"),
computer_guid=compute_node_id)
partition_dict['_software_release_document'] = {
"software_release": software_instance.getUrlString().decode("UTF-8"),
"computer_guid": compute_node_id
}
slap_partition._need_modification = 1
partition_dict["_need_modification"] = 1
parameter_dict = software_instance._asParameterDict(
shared_instance_sql_list=shared_instance_sql_list
)
# software instance has to define an xml parameter
slap_partition._parameter_dict = self._instanceXmlToDict(
partition_dict["_parameter_dict"] = self._instanceXmlToDict(
parameter_dict.pop('xml'))
slap_partition._connection_dict = self._instanceXmlToDict(
partition_dict['_connection_dict'] = self._instanceXmlToDict(
parameter_dict.pop('connection_xml'))
slap_partition._filter_dict = self._instanceXmlToDict(
partition_dict['_filter_dict'] = self._instanceXmlToDict(
parameter_dict.pop('filter_xml'))
slap_partition._instance_guid = parameter_dict.pop('instance_guid')
partition_dict['_instance_guid'] = parameter_dict.pop('instance_guid')
for slave_instance_dict in parameter_dict.get("slave_instance_list", []):
if slave_instance_dict.has_key("connection_xml"):
slave_instance_dict.update(self._instanceXmlToDict(
......@@ -323,9 +305,9 @@ class SlapOSComputeNodeMixin(object):
if slave_instance_dict.has_key("xml"):
slave_instance_dict.update(self._instanceXmlToDict(
slave_instance_dict.pop("xml")))
slap_partition._parameter_dict.update(parameter_dict)
partition_dict['_parameter_dict'].update(parameter_dict)
return slap_partition
return partition_dict
def _getSoftwareInstallationFromUrl(self, url):
software_installation_list = self.getPortalObject().portal_catalog.unrestrictedSearchResults(
......
......@@ -6,12 +6,6 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_recorded_property_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>SlapOSComputeNodeMixin</string> </value>
......@@ -55,28 +49,13 @@
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
<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/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
......@@ -89,7 +68,7 @@
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
......@@ -98,7 +77,7 @@
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
......
......@@ -25,14 +25,11 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
from AccessControl.Permissions import access_contents_information
from AccessControl import getSecurityManager
from AccessControl import Unauthorized
from erp5.component.module.SlapOSCloud import _assertACI
from zLOG import LOG, INFO
from OFS.Traversable import NotFound
try:
from slapos.slap.slap import (
ComputerPartition as SlapComputePartition,
SoftwareRelease)
from slapos.util import calculate_dict_hash
except ImportError:
# Do no prevent instance from starting
......@@ -46,27 +43,48 @@ except ImportError:
def calculate_dict_hash(*args):
raise ImportError
def _assertACI(document):
sm = getSecurityManager()
if sm.checkPermission(access_contents_information,
document):
return document
raise Unauthorized('User %r has no access to %r' % (sm.getUser(), document))
class SlapOSComputePartitionMixin(object):
def _getSoftwareInstance(self, slave_reference=None):
if self.getSlapState() != 'busy':
LOG('SlapOSComputePartitionMixin::_getSoftwareInstance', INFO,
'Compute partition %s shall be busy, is free' %
self.getRelativeUrl())
raise NotFound("No software instance found for: %s - %s" % (
self.getParentValue().getTitle(), self.getTitle()))
else:
query_kw = {
'validation_state': 'validated',
'portal_type': 'Slave Instance',
'default_aggregate_uid': self.getUid(),
}
if slave_reference is None:
query_kw['portal_type'] = "Software Instance"
else:
query_kw['reference'] = slave_reference
class SlapOSComputePartitionMixin(object):
software_instance = _assertACI(
self.getPortalObject().portal_catalog.unrestrictedGetResultValue(**query_kw))
if software_instance is None:
raise NotFound("No software instance found for: %s - %s" % (
self.getParentValue().getTitle(), self.getTitle()))
else:
return software_instance
def _registerComputePartition(self):
portal = self.getPortalObject()
computer_reference = self.getParentValue().getReference()
computer_partition_reference = self.getReference()
compute_node = self
while compute_node.getPortalType() != 'Compute Node':
compute_node = compute_node.getParentValue()
compute_node_id = compute_node.getReference().decode("UTF-8")
slap_partition = SlapComputePartition(computer_reference.decode("UTF-8"),
computer_partition_reference.decode("UTF-8"))
slap_partition._software_release_document = None
slap_partition._requested_state = 'destroyed'
slap_partition._need_modification = 0
software_instance = None
partition_dict = {
"compute_node_id": compute_node_id,
"partition_id": self.getReference().decode("UTF-8"),
"_software_release_document": None,
"_requested_state": 'destroyed',
"_need_modification": 0
}
if self.getSlapState() == 'busy':
software_instance_list = portal.portal_catalog.unrestrictedSearchResults(
......@@ -85,31 +103,31 @@ class SlapOSComputePartitionMixin(object):
self.getRelativeUrl())
if software_instance is not None:
# trick client side, that data has been synchronised already for given
# document
slap_partition._synced = True
state = software_instance.getSlapState()
if state == "stop_requested":
slap_partition._requested_state = 'stopped'
partition_dict['_requested_state'] = 'stopped'
if state == "start_requested":
slap_partition._requested_state = 'started'
partition_dict['_requested_state'] = 'started'
slap_partition._software_release_document = SoftwareRelease(
software_release=software_instance.getUrlString().decode("UTF-8"),
computer_guid=computer_reference.decode("UTF-8"))
slap_partition._need_modification = 1
partition_dict['_software_release_document'] = {
"software_release": software_instance.getUrlString().decode("UTF-8"),
"computer_guid": compute_node_id
}
partition_dict['_access_status'] = software_instance.getTextAccessStatus()
partition_dict["_need_modification"] = 1
# trick client side, that data has been synchronised already for given
# document
partition_dict["_synced"] = True
parameter_dict = software_instance._asParameterDict()
# software instance has to define an xml parameter
slap_partition._parameter_dict = software_instance._instanceXmlToDict(
partition_dict["_parameter_dict"] = software_instance._instanceXmlToDict(
parameter_dict.pop('xml'))
slap_partition._connection_dict = software_instance._instanceXmlToDict(
partition_dict['_connection_dict'] = software_instance._instanceXmlToDict(
parameter_dict.pop('connection_xml'))
slap_partition._filter_dict = software_instance._instanceXmlToDict(
partition_dict['_filter_dict'] = software_instance._instanceXmlToDict(
parameter_dict.pop('filter_xml'))
slap_partition._instance_guid = parameter_dict.pop('instance_guid')
partition_dict['_instance_guid'] = parameter_dict.pop('instance_guid')
for slave_instance_dict in parameter_dict.get("slave_instance_list", []):
if slave_instance_dict.has_key("connection_xml"):
connection_dict = software_instance._instanceXmlToDict(
......@@ -120,6 +138,6 @@ class SlapOSComputePartitionMixin(object):
if slave_instance_dict.has_key("xml"):
slave_instance_dict.update(software_instance._instanceXmlToDict(
slave_instance_dict.pop("xml")))
slap_partition._parameter_dict.update(parameter_dict)
partition_dict['_parameter_dict'].update(parameter_dict)
return slap_partition
return partition_dict
......@@ -6,12 +6,6 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_recorded_property_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>SlapOSComputePartitionMixin</string> </value>
......@@ -61,28 +55,13 @@
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
<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/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
......@@ -95,7 +74,7 @@
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
......@@ -104,7 +83,7 @@
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
......
##############################################################################
#
# Copyright (c) 2021 Nexedi SA and Contributors. All Rights Reserved.
#
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability 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
# garantees and support are strongly adviced 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 AccessControl import getSecurityManager
from zExceptions import Unauthorized
from AccessControl.Permissions import access_contents_information
def _assertACI(document):
sm = getSecurityManager()
if sm.checkPermission(access_contents_information,
document):
return document
raise Unauthorized('User %r has no access to %r' % (sm.getUser(), document))
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Module Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>default_reference</string> </key>
<value> <string>SlapOSCloud</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>module.erp5.SlapOSCloud</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Module 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>
<portal_type id="Catalog Tool">
<item>SlapOSCatalogToolCacheMixin</item>
</portal_type>
<portal_type id="Compute Node">
<item>SlapOSCacheMixin</item>
<item>SlapOSComputeNodeMixin</item>
......
......@@ -35,7 +35,7 @@ import hashlib
from binascii import hexlify
def hashData(data):
return hexlify(hashlib.sha1(data).digest())
return hexlify(hashlib.sha1(json.dumps(data, sort_keys=True)).digest())
class TestSlapOSCloudSlapOSCacheMixin(
SlapOSTestCaseMixin):
......
......@@ -6,12 +6,6 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_recorded_property_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>testSlapOSCloud</string> </value>
......@@ -61,28 +55,13 @@
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
<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/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
......@@ -95,7 +74,7 @@
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
......@@ -104,7 +83,7 @@
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
......
mixin.erp5.SlapOSCacheMixin
mixin.erp5.SlapOSComputeNodeMixin
mixin.erp5.SlapOSComputePartitionMixin
mixin.erp5.SlapOSCatalogToolCacheMixin
\ No newline at end of file
module.erp5.SlapOSCloud
\ No newline at end of file
Catalog Tool | SlapOSCatalogToolCacheMixin
Compute Node | SlapOSCacheMixin
Compute Node | SlapOSComputeNodeMixin
Compute Partition | SlapOSCacheMixin
......
......@@ -15,12 +15,13 @@ import xml.dom.ext
import StringIO
import difflib
import hashlib
import json
from binascii import hexlify
from OFS.Traversable import NotFound
def hashData(data):
return hexlify(hashlib.sha1(data).digest())
return hexlify(hashlib.sha1(json.dumps(data, sort_keys=True)).digest())
class Simulator:
......@@ -87,8 +88,10 @@ class TestSlapOSSlapToolgetFullComputerInformation(TestSlapOSSlapToolMixin):
self.commit()
first_etag = self.compute_node._calculateRefreshEtag()
first_body_fingerprint = hashData(
self.portal_slap._getSlapComputeNodeXMLFromDict(
self.compute_node._getCacheComputeNodeInformation(self.compute_node_id)
)
)
self.assertEqual(200, response.status)
self.assertTrue('last-modified' not in response.headers)
self.assertEqual(first_etag, response.headers.get('etag'))
......@@ -122,8 +125,10 @@ class TestSlapOSSlapToolgetFullComputerInformation(TestSlapOSSlapToolMixin):
self.assertTrue('last-modified' not in response.headers)
second_etag = self.compute_node._calculateRefreshEtag()
second_body_fingerprint = hashData(
self.portal_slap._getSlapComputeNodeXMLFromDict(
self.compute_node._getCacheComputeNodeInformation(self.compute_node_id)
)
)
self.assertNotEqual(first_etag, second_etag)
# The indexation timestamp does not impact the response body
self.assertEqual(first_body_fingerprint, second_body_fingerprint)
......@@ -155,8 +160,10 @@ class TestSlapOSSlapToolgetFullComputerInformation(TestSlapOSSlapToolMixin):
# Edition does not impact the etag
self.assertEqual(second_etag, self.compute_node._calculateRefreshEtag())
third_body_fingerprint = hashData(
self.portal_slap._getSlapComputeNodeXMLFromDict(
self.compute_node._getCacheComputeNodeInformation(self.compute_node_id)
)
)
# The edition impacts the response body
self.assertNotEqual(first_body_fingerprint, third_body_fingerprint)
response = self.portal_slap.getFullComputerInformation(self.compute_node_id)
......@@ -194,7 +201,9 @@ class TestSlapOSSlapToolgetFullComputerInformation(TestSlapOSSlapToolMixin):
# The edition does not impact the response body yet, as the aggregate relation
# is not yet unindex
self.assertEqual(third_body_fingerprint, hashData(
self.portal_slap._getSlapComputeNodeXMLFromDict(
self.compute_node._getCacheComputeNodeInformation(self.compute_node_id)
)
))
response = self.portal_slap.getFullComputerInformation(self.compute_node_id)
self.commit()
......@@ -216,8 +225,10 @@ class TestSlapOSSlapToolgetFullComputerInformation(TestSlapOSSlapToolMixin):
self.assertTrue('last-modified' not in response.headers)
fourth_etag = self.compute_node._calculateRefreshEtag()
fourth_body_fingerprint = hashData(
self.portal_slap._getSlapComputeNodeXMLFromDict(
self.compute_node._getCacheComputeNodeInformation(self.compute_node_id)
)
)
self.assertNotEqual(third_etag, fourth_etag)
# The indexation timestamp does not impact the response body
self.assertNotEqual(third_body_fingerprint, fourth_body_fingerprint)
......
......@@ -6,12 +6,6 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_recorded_property_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>testSlapOSSlapTool</string> </value>
......@@ -55,28 +49,13 @@
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
<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/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
......@@ -89,7 +68,7 @@
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
......@@ -98,7 +77,7 @@
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
......
......@@ -28,10 +28,9 @@
#
##############################################################################
from AccessControl import ClassSecurityInfo
from AccessControl import Unauthorized
from AccessControl.Permissions import access_contents_information
from AccessControl import getSecurityManager
from Products.ERP5Type.UnrestrictedMethod import UnrestrictedMethod
from OFS.Traversable import NotFound
from Products.DCWorkflow.DCWorkflow import ValidationFailed
......@@ -39,10 +38,16 @@ from Products.ERP5Type.Globals import InitializeClass
from Products.ERP5Type.Tool.BaseTool import BaseTool
from Products.ERP5Type import Permissions
from Products.ERP5Type.Cache import CachingMethod
from erp5.component.module.SlapOSCloud import _assertACI
from Products.ERP5Type.Cache import DEFAULT_CACHE_SCOPE
from lxml import etree
try:
from slapos.slap.slap import (
Computer as ComputeNode)
Computer as ComputeNode,
ComputerPartition as SlapComputePartition,
SoftwareInstance as SlapSoftwareInstance,
SoftwareRelease)
from slapos.util import dict2xml, calculate_dict_hash, loads, dumps
except ImportError:
# Do no prevent instance from starting
......@@ -50,6 +55,12 @@ except ImportError:
class ComputeNode:
def __init__(self):
raise ImportError
class SlapComputePartition:
def __init__(self):
raise ImportError
class SoftwareRelease:
def __init__(self):
raise ImportError
def dict2xml(dictionary):
raise ImportError
def calculate_dict_hash(dictionary):
......@@ -58,6 +69,10 @@ except ImportError:
raise ImportError
def dumps(*args):
raise ImportError
class SlapSoftwareInstance:
def __init__(self):
raise ImportError
from zLOG import LOG, INFO
import StringIO
......@@ -111,13 +126,6 @@ def castToStr(dict_kw):
xml_declaration=True, encoding='utf-8')
def _assertACI(document):
sm = getSecurityManager()
if sm.checkPermission(access_contents_information,
document):
return document
raise Unauthorized('User %r has no access to %r' % (sm.getUser(), document))
_MARKER = object()
......@@ -163,20 +171,56 @@ class SlapTool(BaseTool):
Reuses slap library for easy marshalling.
"""
user = self.getPortalObject().portal_membership.getAuthenticatedMember().getUserName()
portal = self.getPortalObject()
user = portal.portal_membership.getAuthenticatedMember().getUserName()
if str(user) == computer_id:
compute_node = self.getPortalObject().portal_membership.getAuthenticatedMember().getUserValue()
compute_node = portal.portal_membership.getAuthenticatedMember().getUserValue()
compute_node.setAccessStatus(computer_id)
else:
# Don't use getDocument because we don't want use _assertACI here, but
# just call the API on computer.
compute_node = self.getPortalObject().portal_catalog.unrestrictedSearchResults(
compute_node = portal.portal_catalog.unrestrictedSearchResults(
portal_type='Compute Node', reference=computer_id,
validation_state="validated")[0].getObject()
refresh_etag = compute_node._calculateRefreshEtag()
body, etag = compute_node._getComputeNodeInformation(user, refresh_etag)
# Keep compatibility with older clients that relies on marshalling.
# This code is an adaptation of SlapOSComputeNodeMixin._getComputeNodeInformation
# To comply with cache capability.
user_document = _assertACI(portal.portal_catalog.unrestrictedGetResultValue(
reference=user, portal_type=['Person', 'Compute Node', 'Software Instance']))
user_type = user_document.getPortalType()
if user_type in ('Compute Node', 'Person'):
if not compute_node._isTestRun():
cache_plugin = compute_node._getCachePlugin()
key = '%s_%s' % (compute_node.getReference(), user)
try:
entry = cache_plugin.get(key, DEFAULT_CACHE_SCOPE)
except KeyError:
entry = None
if entry is not None and isinstance(entry.getValue(), dict):
cached_dict = entry.getValue()
cached_etag = cached_dict.get('refresh_etag', None)
if (refresh_etag != cached_etag):
# Do not recalculate the compute_node information
# if nothing changed
compute_node._activateFillComputeNodeInformationCache(user)
etag = cached_etag
body = cached_dict['data_xml']
else:
compute_node._activateFillComputeNodeInformationCache(user)
self.REQUEST.response.setStatus(503)
return self.REQUEST.response
else:
computer_dict, etag = compute_node._getComputeNodeInformation(user, refresh_etag)
body = self._getSlapComputeNodeXMLFromDict(computer_dict)
else:
computer_dict, etag = compute_node._getComputeNodeInformation(user, refresh_etag)
body = self._getSlapComputeNodeXMLFromDict(computer_dict)
self.REQUEST.response.setHeader('Content-Type', 'text/xml; charset=utf-8')
if self.REQUEST.response.getStatus() == 200:
# Keep in cache server for 7 days
self.REQUEST.response.setHeader('Cache-Control',
......@@ -275,7 +319,7 @@ class SlapTool(BaseTool):
"""
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()
# Keep in cache server for 7 days
......@@ -295,7 +339,7 @@ class SlapTool(BaseTool):
"""
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
compute_node = _assertACI(compute_node)
try:
......@@ -574,7 +618,7 @@ class SlapTool(BaseTool):
'doc/computer_consumption.xsd')
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)
source_reference = \
tree.find('transaction').find('reference').text or ""
......@@ -595,7 +639,7 @@ class SlapTool(BaseTool):
"""
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)
security.declareProtected(Permissions.AccessContentsInformation,
......@@ -611,13 +655,13 @@ class SlapTool(BaseTool):
def loadComputerConfigurationFromXML(self, xml):
"Load the given xml as configuration for the compute_node object"
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)
return 'Content properly posted.'
@convertToREST
def _generateComputerCertificate(self, compute_node_id):
self._getComputeNodeDocument(compute_node_id).generateCertificate()
self.getPortalObject().portal_catalog.getComputeNodeObject(compute_node_id).generateCertificate()
result = {
'certificate': self.REQUEST.get('compute_node_certificate').decode("UTF-8"),
'key': self.REQUEST.get('compute_node_key').decode("UTF-8")
......@@ -632,7 +676,7 @@ class SlapTool(BaseTool):
@convertToREST
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,
'revokeComputerCertificate')
......@@ -650,10 +694,28 @@ class SlapTool(BaseTool):
"""
# Try to get the compute partition to raise an exception if it doesn't
# exist
compute_partition_document = self._getComputePartitionDocument(
compute_partition_document = self.getPortalObject().portal_catalog.getComputePartitionObject(
computer_reference, computer_partition_reference)
slap_compute_partition = compute_partition_document._registerComputePartition()
partition_dict = compute_partition_document._registerComputePartition()
slap_compute_partition = SlapComputePartition(
partition_id=partition_dict["partition_id"],
computer_id=partition_dict['compute_node_id']
)
slap_compute_partition._requested_state = partition_dict["_requested_state"]
slap_compute_partition._need_modification = partition_dict["_need_modification"]
if partition_dict["_software_release_document"] is not None:
slap_compute_partition._parameter_dict = partition_dict["_parameter_dict"]
slap_compute_partition._connection_dict = partition_dict["_connection_dict"]
slap_compute_partition._filter_dict = partition_dict["_filter_dict"]
slap_compute_partition._instance_guid = partition_dict["_instance_guid"]
slap_compute_partition._software_release_document = SoftwareRelease(
software_release=partition_dict["_software_release_document"]["software_release"],
computer_guid=partition_dict["_software_release_document"]["computer_guid"])
slap_compute_partition._synced = partition_dict["_synced"]
else:
slap_compute_partition._software_release_document = None
# Keep in cache server for 7 days
self.REQUEST.response.setStatus(200)
......@@ -670,7 +732,6 @@ class SlapTool(BaseTool):
####################################################
# Internal methods
####################################################
def _validateXML(self, to_be_validated, xsd_model):
"""Will validate the xml file"""
#We parse the XSD model
......@@ -698,7 +759,7 @@ class SlapTool(BaseTool):
"""
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)
@convertToREST
......@@ -706,7 +767,7 @@ class SlapTool(BaseTool):
"""
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.setBuildingStatus(
'software release %s' % url, "building")
......@@ -716,7 +777,7 @@ class SlapTool(BaseTool):
"""
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.setAccessStatus(
'software release %s available' % url, "available")
......@@ -726,7 +787,7 @@ class SlapTool(BaseTool):
"""
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)
if software_installation.getSlapState() != 'destroy_requested':
raise NotFound
......@@ -923,7 +984,8 @@ class SlapTool(BaseTool):
if not requested_software_instance.getAggregate(portal_type="Compute Partition"):
raise SoftwareInstanceNotReady
else:
return dumps(requested_software_instance._asSoftwareInstance())
return dumps(SlapSoftwareInstance(
**requested_software_instance._asSoftwareInstanceDict()))
@UnrestrictedMethod
def _updateComputePartitionRelatedInstanceList(self, compute_node_id,
......@@ -947,92 +1009,56 @@ class SlapTool(BaseTool):
# 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
def _getSlapComputeNodeXMLFromDict(self, computer_dict):
slap_compute_node = ComputeNode(computer_dict["_computer_id"])
slap_compute_node._computer_partition_list = []
slap_compute_node._software_release_list = []
for partition_dict in computer_dict["_computer_partition_list"]:
slap_compute_partition = SlapComputePartition(
partition_id=partition_dict["partition_id"],
computer_id=partition_dict['compute_node_id']
)
slap_compute_partition._requested_state = partition_dict["_requested_state"]
slap_compute_partition._need_modification = partition_dict["_need_modification"]
if partition_dict["_software_release_document"] is not None:
slap_compute_partition._access_status = partition_dict["_access_status"]
slap_compute_partition._parameter_dict = partition_dict["_parameter_dict"]
slap_compute_partition._connection_dict = partition_dict["_connection_dict"]
slap_compute_partition._filter_dict = partition_dict["_filter_dict"]
slap_compute_partition._instance_guid = partition_dict["_instance_guid"]
slap_compute_partition._software_release_document = SoftwareRelease(
software_release=partition_dict["_software_release_document"]["software_release"],
computer_guid=partition_dict["_software_release_document"]["computer_guid"])
else:
return _assertACI(l[0].getObject())
slap_compute_partition._software_release_document = None
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()
slap_compute_node._computer_partition_list.append(
slap_compute_partition
)
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)
for software_release_dict in computer_dict['_software_release_list']:
slap_software_release = SoftwareRelease(
software_release=software_release_dict["software_release"],
computer_guid=software_release_dict['computer_guid'])
slap_software_release._requested_state = software_release_dict['_requested_state']
slap_software_release._known_state = software_release_dict['_known_state']
slap_compute_node._software_release_list.append(slap_software_release)
@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))
return dumps(slap_compute_node)
def _getSoftwareInstanceForComputePartition(self, compute_node_id,
compute_partition_id, slave_reference=None):
compute_partition_document = self._getComputePartitionDocument(
compute_node_id, compute_partition_id)
if compute_partition_document.getSlapState() != 'busy':
LOG('SlapTool::_getSoftwareInstanceForComputePartition', INFO,
'Compute partition %s shall be busy, is free' %
compute_partition_document.getRelativeUrl())
raise NotFound, "No software instance found for: %s - %s" % (compute_node_id,
compute_partition_id)
else:
query_kw = {
'validation_state': 'validated',
'portal_type': 'Slave Instance',
'default_aggregate_uid': compute_partition_document.getUid(),
}
if slave_reference is None:
query_kw['portal_type'] = "Software Instance"
else:
query_kw['reference'] = slave_reference
software_instance = _assertACI(self.getPortalObject().portal_catalog.unrestrictedGetResultValue(**query_kw))
if software_instance is None:
raise NotFound, "No software instance found for: %s - %s" % (
compute_partition_document = self.getPortalObject().portal_catalog.getComputePartitionObject(
compute_node_id, compute_partition_id)
else:
return software_instance
return compute_partition_document._getSoftwareInstance(slave_reference)
@convertToREST
def _softwareReleaseError(self, url, compute_node_id, error_log):
"""
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.setErrorStatus('while installing %s' % url)
......
......@@ -6,12 +6,6 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_recorded_property_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>SlapTool</string> </value>
......@@ -55,28 +49,13 @@
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
<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/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
......@@ -89,7 +68,7 @@
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
......@@ -98,7 +77,7 @@
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
......
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