Commit 88bdb2d7 authored by Rafael Monnerat's avatar Rafael Monnerat

Slaptool Major clean up

See merge request nexedi/slapos.core!368
parents 839ac0ef 5804e551
......@@ -31,6 +31,32 @@ 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 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
......@@ -52,12 +78,9 @@ class SoftwareInstance(Item):
def _getXmlAsDict(self, xml):
result_dict = {}
if xml is None or xml == '':
return result_dict
if xml:
tree = etree.fromstring(xml)
for element in tree.findall('parameter'):
for element in tree.iterfind('parameter'):
key = element.get('id').encode("UTF-8")
value = result_dict.get(key, None)
if value is not None:
......@@ -127,3 +150,172 @@ class SoftwareInstance(Item):
if size != len(visited) + 1:
raise DisconnectedSoftwareTree
return True
def _instanceXmlToDict(self, xml):
result_dict = {}
try:
result_dict = xml2dict(xml)
except (etree.XMLSchemaError, etree.XMLSchemaParseError, # pylint: disable=catching-non-exception
etree.XMLSchemaValidateError, etree.XMLSyntaxError): # pylint: disable=catching-non-exception
LOG('SoftwareInstance', INFO, 'Issue during parsing xml:', error=True)
return result_dict
def _asSoftwareInstance(self):
parameter_dict = self._asParameterDict()
requested_state = self.getSlapState()
if requested_state == "stop_requested":
state = 'stopped'
elif requested_state == "start_requested":
state = 'started'
elif requested_state == "destroy_requested":
state = 'destroyed'
else:
raise ValueError("Unknown slap state : %s" % requested_state)
# software instance has to define an xml parameter
xml = self._instanceXmlToDict(
parameter_dict.pop('xml'))
connection_xml = self._instanceXmlToDict(
parameter_dict.pop('connection_xml'))
filter_xml = self._instanceXmlToDict(
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
@UnrestrictedMethod
def _asParameterDict(self, shared_instance_sql_list=None):
portal = self.getPortalObject()
compute_partition = self.getAggregateValue(portal_type="Compute Partition")
if compute_partition is None:
raise ValueError("Instance isn't allocated to call _asParamterDict")
timestamp = int(compute_partition.getModificationDate())
newtimestamp = int(self.getBangTimestamp(int(self.getModificationDate())))
if (newtimestamp > timestamp):
timestamp = newtimestamp
instance_tree = self.getSpecialiseValue()
ip_list = []
full_ip_list = []
for internet_protocol_address in compute_partition.contentValues(portal_type='Internet Protocol Address'):
# XXX - There is new values, and we must keep compatibility
address_tuple = (
internet_protocol_address.getNetworkInterface('').decode("UTF-8"),
internet_protocol_address.getIpAddress().decode("UTF-8"))
if internet_protocol_address.getGatewayIpAddress('') and \
internet_protocol_address.getNetmask(''):
address_tuple = address_tuple + (
internet_protocol_address.getGatewayIpAddress().decode("UTF-8"),
internet_protocol_address.getNetmask().decode("UTF-8"),
internet_protocol_address.getNetworkAddress('').decode("UTF-8"))
full_ip_list.append(address_tuple)
else:
ip_list.append(address_tuple)
shared_instance_list = []
if (self.getPortalType() == "Software Instance"):
append = shared_instance_list.append
if shared_instance_sql_list is None:
shared_instance_sql_list = portal.portal_catalog.unrestrictedSearchResults(
default_aggregate_uid=compute_partition.getUid(),
portal_type='Slave Instance',
validation_state="validated",
**{"slapos_item.slap_state": "start_requested"}
)
for shared_instance in shared_instance_sql_list:
shared_instance = _assertACI(shared_instance.getObject())
# XXX Use catalog to filter more efficiently
if shared_instance.getSlapState() == "start_requested":
newtimestamp = int(shared_instance.getBangTimestamp(int(shared_instance.getModificationDate())))
append({
'slave_title': shared_instance.getTitle().decode("UTF-8"),
'slap_software_type': \
shared_instance.getSourceReference().decode("UTF-8"),
'slave_reference': shared_instance.getReference().decode("UTF-8"),
'timestamp': newtimestamp,
'xml': shared_instance.getTextContent(),
'connection_xml': shared_instance.getConnectionXml(),
})
if (newtimestamp > timestamp):
timestamp = newtimestamp
return {
'instance_guid': self.getReference().decode("UTF-8"),
'instance_title': self.getTitle().decode("UTF-8"),
'root_instance_title': instance_tree.getTitle().decode("UTF-8"),
'root_instance_short_title': instance_tree.getShortTitle().decode("UTF-8"),
'xml': self.getTextContent(),
'connection_xml': self.getConnectionXml(),
'filter_xml': self.getSlaXml(),
'slap_computer_id': \
compute_partition.getParentValue().getReference().decode("UTF-8"),
'slap_computer_partition_id': \
compute_partition.getReference().decode("UTF-8"),
'slap_software_type': \
self.getSourceReference().decode("UTF-8"),
'slap_software_release_url': \
self.getUrlString().decode("UTF-8"),
'slave_instance_list': shared_instance_list,
'ip_list': ip_list,
'full_ip_list': full_ip_list,
'timestamp': "%i" % timestamp,
}
@UnrestrictedMethod
def _getInstanceTreeIpList(self):
if self.getSlapState() == 'destroy_requested':
return []
# Search instance tree
instance_tree = self.getSpecialiseValue()
while instance_tree and instance_tree.getPortalType() != "Instance Tree":
instance_tree = instance_tree.getSpecialiseValue()
ip_address_list = []
for instance in instance_tree.getSpecialiseRelatedValueList(
portal_type="Software Instance"):
compute_partition = instance.getAggregateValue(portal_type="Compute Partition")
if not compute_partition:
continue
for internet_protocol_address in compute_partition.contentValues(
portal_type='Internet Protocol Address'):
ip_address_list.append(
(internet_protocol_address.getNetworkInterface('').decode("UTF-8"),
internet_protocol_address.getIpAddress().decode("UTF-8"))
)
return ip_address_list
def _updateSucessorList(self, instance_reference_xml):
"""
Update Software Instance successor list to match the given list. If one
instance was not requested by this compute partition, it should be removed
in the successor_list of this instance.
Once the link is removed, this instance will be trashed by Garbage Collect!
instance_reference_xml contain list of title of sub-instances requested by
this instance.
"""
cache_reference = '%s-PREDLIST' % self.getReference()
if not self.isLastData(cache_reference, instance_reference_xml):
instance_reference_list = loads(instance_reference_xml)
current_successor_list = self.getSuccessorValueList(
portal_type=['Software Instance', 'Slave Instance'])
current_successor_title_list = [i.getTitle() for i in current_successor_list]
# If there are items to remove
if list(set(current_successor_title_list).difference(instance_reference_list)) != []:
successor_list = [instance.getRelativeUrl() for instance in
current_successor_list if instance.getTitle()
in instance_reference_list]
LOG('SoftwareInstance', INFO, '%s : Updating successor list to %s' % (
self.getReference(), successor_list), error=False)
self.edit(successor_list=successor_list,
comment='successor_list edited to unlink non commited instances')
self.setLastData(instance_reference_xml, key=cache_reference)
\ No newline at end of file
......@@ -6,10 +6,22 @@
</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>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>document.erp5.SoftwareInstance</string> </value>
......@@ -43,13 +55,28 @@
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
<persistent> <string encoding="base64">AAAAAAAAAAM=</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>
......@@ -62,7 +89,7 @@
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
</dictionary>
......@@ -71,13 +98,15 @@
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.patches.WorkflowTool"/>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<tuple>
<none/>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
......@@ -90,7 +119,9 @@
</item>
</dictionary>
</list>
</tuple>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -35,6 +35,10 @@ from Products.ERP5Type.Cache import DEFAULT_CACHE_SCOPE
import json
ACCESS = "#access"
ERROR = "#error"
BUILDING = "#building"
class SlapOSCacheMixin:
# Declarative security
......@@ -93,7 +97,16 @@ class SlapOSCacheMixin:
return data_dict
def setAccessStatus(self, text, state=""):
def setAccessStatus(self, text, state="", reindex=0):
return self._setAccessStatus("%s %s" % (ACCESS, text), state, reindex)
def setErrorStatus(self, text, state="", reindex=0):
return self._setAccessStatus("%s %s" % (ERROR, text), state, reindex)
def setBuildingStatus(self, text, state="", reindex=0):
return self._setAccessStatus("%s %s" % (BUILDING, text), state, reindex)
def _setAccessStatus(self, text, state="", reindex=0):
user_reference = self.getPortalObject().portal_membership.getAuthenticatedMember()\
.getUserName()
......@@ -121,6 +134,9 @@ class SlapOSCacheMixin:
cache_duration = self._getAccessStatusCacheFactory().cache_duration
self._getAccessStatusPlugin().set(self._getAccessStatusCacheKey(),
DEFAULT_CACHE_SCOPE, value, cache_duration=cache_duration)
if status_changed and reindex:
self.reindexObject()
return status_changed
def getTextAccessStatus(self):
......@@ -134,7 +150,6 @@ class SlapOSCacheMixin:
date = DateTime(data_dict['created_at'])
return date.strftime('%Y/%m/%d %H:%M')
#####################
# SlapOS Last Data
#####################
......@@ -166,3 +181,6 @@ class SlapOSCacheMixin:
else:
entry = entry.getValue()
return entry
def isLastData(self, key=None, value=None):
return self.getLastData(key) == value
\ No newline at end of file
# -*- 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 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
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
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):
def _getCachePlugin(self):
return self.getPortalObject().portal_caches\
.getRamCacheRoot().get('compute_node_information_cache_factory')\
.getCachePluginList()[0]
@UnrestrictedMethod
def _getSoftwareReleaseValueList(self):
"""Returns list of Software Releases documents for compute_node"""
portal = self.getPortalObject()
software_release_list = []
for software_installation in portal.portal_catalog.unrestrictedSearchResults(
portal_type='Software Installation',
default_aggregate_uid=self.getUid(),
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'))
if software_installation.getSlapState() == 'destroy_requested':
software_release_response._requested_state = 'destroyed'
else:
software_release_response._requested_state = 'available'
known_state = software_installation.getTextAccessStatus()
if known_state.startswith("#access"):
software_release_response._known_state = 'available'
elif known_state.startswith("#building"):
software_release_response._known_state = 'building'
else:
software_release_response._known_state = 'error'
software_release_list.append(software_release_response)
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()
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)
def _activateFillComputeNodeInformationCache(self, user):
tag = 'compute_node_information_cache_fill_%s_%s' % (self.getReference(), user)
if self.getPortalObject().portal_activities.countMessageWithTag(tag) == 0:
self.activate(activity='SQLQueue', priority=2, tag=tag)._fillComputeNodeInformationCache(user)
def _fillComputeNodeInformationCache(self, user):
key = '%s_%s' % (self.getReference(), user)
try:
self._getCachePlugin().set(key, DEFAULT_CACHE_SCOPE,
dict (
time=time.time(),
refresh_etag=self._calculateRefreshEtag(),
data=self._getCacheComputeNodeInformation(user),
),
cache_duration=self.getPortalObject().portal_caches\
.getRamCacheRoot().get('compute_node_information_cache_factory'\
).cache_duration
)
except (Unauthorized, IndexError):
# XXX: Unauthorized hack. Race condition of not ready setup delivery which provides
# security information shall not make this method fail, as it will be
# called later anyway
# Note: IndexError ignored, as it happend in case if full reindex is
# called on site
pass
def _calculateRefreshEtag(self):
# check max indexation timestamp
# it is unlikely to get an empty catalog
last_indexed_entry = self.getPortalObject().portal_catalog(
select_list=['indexation_timestamp'],
portal_type=['Compute Node', 'Compute Partition',
'Software Instance', 'Slave Instance',
'Software Installation'],
sort_on=[('indexation_timestamp', 'DESC')],
limit=1,
)[0]
return '%s_%s' % (last_indexed_entry.uid,
last_indexed_entry.indexation_timestamp)
def _isTestRun(self):
if self.REQUEST.get('disable_isTestRun', False):
return False
if issubclass(self.getPortalObject().MailHost.__class__, DummyMailHostMixin) \
or self.REQUEST.get('test_list'):
return True
return False
def _getComputeNodeInformation(self, user, refresh_etag):
portal = self.getPortalObject()
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()
key = '%s_%s' % (self.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
self._activateFillComputeNodeInformationCache(user)
return cached_dict['data'], cached_etag
else:
self._activateFillComputeNodeInformationCache(user)
self.REQUEST.response.setStatus(503)
return self.REQUEST.response, None
else:
return self._getCacheComputeNodeInformation(user), None
else:
slap_compute_node._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(
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
def _calculateSlapComputeNodeInformation(self, slap_compute_node, compute_partition_list):
if len(compute_partition_list) == 0:
return
unrestrictedSearchResults = self.getPortalObject().portal_catalog.unrestrictedSearchResults
compute_partition_uid_list = [x.uid for x in compute_partition_list]
grouped_software_instance_list = unrestrictedSearchResults(
portal_type="Software Instance",
default_aggregate_uid=compute_partition_uid_list,
validation_state="validated",
group_by_list=['default_aggregate_uid'],
select_list=['default_aggregate_uid', 'count(*)']
)
slave_software_instance_list = unrestrictedSearchResults(
default_aggregate_uid=compute_partition_uid_list,
portal_type='Slave Instance',
validation_state="validated",
select_list=['default_aggregate_uid'],
**{"slapos_item.slap_state": "start_requested"}
)
for compute_partition in compute_partition_list:
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(
self._getSlapPartitionByPackingList(
_assertACI(compute_partition.getObject()),
software_instance_list,
[x for x in slave_software_instance_list if (x.default_aggregate_uid == compute_partition.getUid())]
)
)
def _instanceXmlToDict(self, xml):
result_dict = {}
try:
result_dict = xml2dict(xml)
except (etree.XMLSchemaError, etree.XMLSchemaParseError, # pylint: disable=catching-non-exception
etree.XMLSchemaValidateError, etree.XMLSyntaxError): # pylint: disable=catching-non-exception
LOG('SlapOSComputeNodeCacheMixin', INFO, 'Issue during parsing xml:', error=True)
return result_dict
def _getSlapPartitionByPackingList(self, compute_partition_document,
software_instance_list,
shared_instance_sql_list):
compute_node = compute_partition_document
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
software_instance = None
if compute_partition_document.getSlapState() == 'busy':
software_instance_count = len(software_instance_list)
if software_instance_count == 1:
software_instance = _assertACI(software_instance_list[0].getObject())
elif software_instance_count > 1:
# XXX do not prevent the system to work if one partition is broken
raise NotImplementedError, "Too many instances linked to %s" % \
compute_partition_document.getRelativeUrl()
if software_instance is not None:
state = software_instance.getSlapState()
if state == "stop_requested":
slap_partition._requested_state = 'stopped'
if state == "start_requested":
slap_partition._requested_state = 'started'
slap_partition._access_status = software_instance.getTextAccessStatus()
slap_partition._software_release_document = SoftwareRelease(
software_release=software_instance.getUrlString().decode("UTF-8"),
computer_guid=compute_node_id)
slap_partition._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(
parameter_dict.pop('xml'))
slap_partition._connection_dict = self._instanceXmlToDict(
parameter_dict.pop('connection_xml'))
slap_partition._filter_dict = self._instanceXmlToDict(
parameter_dict.pop('filter_xml'))
slap_partition._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(
slave_instance_dict.pop("connection_xml")))
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)
return slap_partition
def _getSoftwareInstallationFromUrl(self, url):
software_installation_list = self.getPortalObject().portal_catalog.unrestrictedSearchResults(
portal_type='Software Installation',
default_aggregate_uid=self.getUid(),
validation_state='validated',
limit=2,
url_string={'query': url, 'key': 'ExactMatch'},
)
l = len(software_installation_list)
if l == 1:
return _assertACI(software_installation_list[0].getObject())
elif l == 0:
raise NotFound('No software release %r found on compute_node %r' % (url,
self.getReference()))
else:
raise ValueError('Wrong list of software releases on %r: %s' % (
self.getReference(), ', '.join([q.getRelativeUrl() for q \
in software_installation_list])
))
<?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>_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>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>mixin.erp5.SlapOSComputeNodeMixin</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">AAAAAAAAAAM=</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>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<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>error_message</string> </key>
<value>
<list>
<persistent> <string encoding="base64">AAAAAAAAAAU=</string> </persistent>
</list>
</value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="5" aka="AAAAAAAAAAU=">
<pickle>
<global name="Message" module="Products.ERP5Type.Message"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>default</string> </key>
<value> <string>Class ${reference} must be defined</string> </value>
</item>
<item>
<key> <string>domain</string> </key>
<value> <string>erp5_ui</string> </value>
</item>
<item>
<key> <string>mapping</string> </key>
<value>
<dictionary>
<item>
<key> <string>reference</string> </key>
<value> <string>SlapOSComputeNodeCacheMixin</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>message</string> </key>
<value> <string>Class ${reference} must be defined</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
# -*- 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 AccessControl.Permissions import access_contents_information
from AccessControl import getSecurityManager
from AccessControl import Unauthorized
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
# if libs are not installed
class SlapComputePartition:
def __init__(self):
raise ImportError
class SoftwareRelease:
def __init__(self):
raise 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 _registerComputePartition(self):
portal = self.getPortalObject()
computer_reference = self.getParentValue().getReference()
computer_partition_reference = self.getReference()
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
if self.getSlapState() == 'busy':
software_instance_list = portal.portal_catalog.unrestrictedSearchResults(
portal_type="Software Instance",
default_aggregate_uid=self.getUid(),
validation_state="validated",
limit=2,
)
software_instance_count = len(software_instance_list)
if software_instance_count == 1:
software_instance = _assertACI(software_instance_list[0].getObject())
elif software_instance_count > 1:
# XXX do not prevent the system to work if one partition is broken
raise NotImplementedError, "Too many instances %s linked to %s" % \
([x.path for x in software_instance_list],
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'
if state == "start_requested":
slap_partition._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
parameter_dict = software_instance._asParameterDict()
# software instance has to define an xml parameter
slap_partition._parameter_dict = software_instance._instanceXmlToDict(
parameter_dict.pop('xml'))
slap_partition._connection_dict = software_instance._instanceXmlToDict(
parameter_dict.pop('connection_xml'))
slap_partition._filter_dict = software_instance._instanceXmlToDict(
parameter_dict.pop('filter_xml'))
slap_partition._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(
slave_instance_dict.pop("connection_xml"))
slave_instance_dict.update(connection_dict)
slave_instance_dict['connection-parameter-hash'] = \
calculate_dict_hash(connection_dict)
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)
return slap_partition
<?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>_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>
</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.SlapOSComputePartitionMixin</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">AAAAAAAAAAM=</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>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<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="Compute Node">
<item>SlapOSCacheMixin</item>
<item>SlapOSComputeNodeMixin</item>
</portal_type>
<portal_type id="Compute Partition">
<item>SlapOSCacheMixin</item>
<item>SlapOSComputePartitionMixin</item>
</portal_type>
<portal_type id="Person">
<item>SlapOSCacheMixin</item>
......
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 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 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 erp5.component.test.SlapOSTestCaseMixin import SlapOSTestCaseMixin
from DateTime import DateTime
from App.Common import rfc1123_date
import json
import hashlib
from binascii import hexlify
def hashData(data):
return hexlify(hashlib.sha1(data).digest())
class TestSlapOSCloudSlapOSCacheMixin(
SlapOSTestCaseMixin):
def afterSetUp(self):
SlapOSTestCaseMixin.afterSetUp(self)
self.pinDateTime(DateTime())
self._makeComputeNode()
self._makeComplexComputeNode(with_slave=True)
self.tic()
def beforeTearDown(self):
self.unpinDateTime()
self._cleaupREQUEST()
def test_LastData(self):
value = "XXX"
self.assertEqual(None, self.compute_node.getLastData())
self.compute_node.setLastData(value)
self.assertEqual(value, self.compute_node.getLastData())
key = "OI"
value_key = "ABC"
self.assertEqual(None, self.compute_node.getLastData(key))
self.compute_node.setLastData(value_key, key=key)
self.assertEqual(value_key, self.compute_node.getLastData(key))
def test_getAccessStatus_no_data(self):
since = rfc1123_date(DateTime())
created_at = since
def getBaseExpectedDict(doc):
return {
"user": "SlapOS Master",
'created_at': '%s' % created_at,
'since': '%s' % since,
'state': "",
"text": "#error no data found for %s" % doc.getReference(),
"no_data": 1
}
# Check Compute Node
self.assertEqual(self.compute_node._getCachedAccessInfo(), None)
self.assertEqual(self.compute_node.getAccessStatus(),
getBaseExpectedDict(self.compute_node))
# Check Software Installation
installation = self.start_requested_software_installation
self.assertEqual(installation._getCachedAccessInfo(), None)
self.assertEqual(installation.getAccessStatus(),
getBaseExpectedDict(installation))
partition = self.compute_node.partition1
self.assertEqual(partition._getCachedAccessInfo(), None)
self.assertEqual(partition.getAccessStatus(),
getBaseExpectedDict(partition))
instance = self.start_requested_software_instance
self.assertEqual(instance._getCachedAccessInfo(), None)
self.assertEqual(instance.getAccessStatus(),
getBaseExpectedDict(instance))
def test_setAccessStatus(self):
since = rfc1123_date(DateTime())
created_at = since
def getExpectedCacheDict(doc):
return json.dumps({
"user": "ERP5TypeTestCase",
'created_at': '%s' % created_at,
'since': '%s' % since,
'state': "",
"text": "#access TEST123 %s" % doc.getUid()
})
def getBaseExpectedDict(doc):
return {
"user": "ERP5TypeTestCase",
'created_at': '%s' % created_at,
u'since': u'%s' % since,
u'state': u"",
u"text": u"#access TEST123 %s" % doc.getUid(),
'no_data_since_15_minutes': 0,
'no_data_since_5_minutes': 0
}
# Check Compute Node
self.assertEqual(True,
self.compute_node.setAccessStatus("TEST123 %s" % self.compute_node.getUid()))
self.assertEqual(self.compute_node._getCachedAccessInfo(),
getExpectedCacheDict(self.compute_node))
self.assertEqual(self.compute_node.getAccessStatus(),
getBaseExpectedDict(self.compute_node))
self.assertEqual(False,
self.compute_node.setAccessStatus("TEST123 %s" % self.compute_node.getUid()))
# Check Software Installation
installation = self.start_requested_software_installation
self.assertEqual(True,
installation.setAccessStatus("TEST123 %s" % installation.getUid()))
self.assertEqual(installation._getCachedAccessInfo(),
getExpectedCacheDict(installation))
self.assertEqual(installation.getAccessStatus(),
getBaseExpectedDict(installation))
self.assertEqual(False,
installation.setAccessStatus("TEST123 %s" % installation.getUid()))
# Compute Partition
partition = self.compute_node.partition1
self.assertEqual(True,
partition.setAccessStatus("TEST123 %s" % partition.getUid()))
self.assertEqual(partition._getCachedAccessInfo(),
getExpectedCacheDict(partition))
self.assertEqual(partition.getAccessStatus(),
getBaseExpectedDict(partition))
self.assertEqual(False,
partition.setAccessStatus("TEST123 %s" % partition.getUid()))
# Software Instance
instance = self.start_requested_software_instance
# This is already called from elsewhere, so it actually changed
self.assertEqual(True,
instance.setAccessStatus("TEST123 %s" % instance.getUid()))
self.assertEqual(instance._getCachedAccessInfo(),
getExpectedCacheDict(instance))
self.assertEqual(instance.getAccessStatus(),
getBaseExpectedDict(instance))
self.assertEqual(False,
instance.setAccessStatus("TEST123 %s" % instance.getUid()))
def test_setAccessStatus_reindex(self):
since = rfc1123_date(DateTime())
created_at = since
def getExpectedCacheDict(doc):
return json.dumps({
"user": "ERP5TypeTestCase",
'created_at': '%s' % created_at,
'since': '%s' % since,
'state': "",
"text": "#access TEST123 %s" % doc.getUid()
})
def getBaseExpectedDict(doc):
return {
"user": "ERP5TypeTestCase",
'created_at': '%s' % created_at,
u'since': u'%s' % since,
u'state': u"",
u"text": u"#access TEST123 %s" % doc.getUid(),
'no_data_since_15_minutes': 0,
'no_data_since_5_minutes': 0
}
self.tic()
# Software Instance
instance = self.start_requested_software_instance
indexation_timestamp = self.portal.portal_catalog(
uid=instance.getUid(),
select_dict={"indexation_timestamp": None}
)[0].indexation_timestamp
# This is already called from elsewhere, so it actually changed
self.assertEqual(True,
instance.setAccessStatus("TEST123 %s" % instance.getUid()))
self.assertEqual(instance._getCachedAccessInfo(),
getExpectedCacheDict(instance))
self.assertEqual(instance.getAccessStatus(),
getBaseExpectedDict(instance))
self.tic()
new_indexation_timestamp = self.portal.portal_catalog(
uid=instance.getUid(),
select_dict={"indexation_timestamp": None}
)[0].indexation_timestamp
self.assertEqual(new_indexation_timestamp, indexation_timestamp)
self.assertEqual(False,
instance.setAccessStatus("TEST123 %s" % instance.getUid()))
self.tic()
new_indexation_timestamp = self.portal.portal_catalog(
uid=instance.getUid(),
select_dict={"indexation_timestamp": None}
)[0].indexation_timestamp
self.assertEqual(new_indexation_timestamp, indexation_timestamp)
self.assertEqual(False,
instance.setAccessStatus("TEST123 %s" % instance.getUid(), reindex=1))
self.tic()
new_indexation_timestamp = self.portal.portal_catalog(
uid=instance.getUid(),
select_dict={"indexation_timestamp": None}
)[0].indexation_timestamp
self.assertEqual(new_indexation_timestamp, indexation_timestamp)
self.assertEqual(True,
instance.setErrorStatus("TEST123 %s" % instance.getUid(), reindex=1))
self.tic()
new_indexation_timestamp = self.portal.portal_catalog(
uid=instance.getUid(),
select_dict={"indexation_timestamp": None}
)[0].indexation_timestamp
self.assertNotEqual(new_indexation_timestamp, indexation_timestamp)
def test_setErrorStatus(self):
since = rfc1123_date(DateTime())
created_at = since
def getExpectedCacheDict(doc):
return json.dumps({
"user": "ERP5TypeTestCase",
'created_at': '%s' % created_at,
'since': '%s' % since,
'state': "",
"text": "#error TEST123 %s" % doc.getUid()
})
def getBaseExpectedDict(doc):
return {
"user": "ERP5TypeTestCase",
'created_at': '%s' % created_at,
u'since': u'%s' % since,
u'state': u"",
u"text": u"#error TEST123 %s" % doc.getUid(),
'no_data_since_15_minutes': 0,
'no_data_since_5_minutes': 0
}
# Check Compute Node
self.assertEqual(True,
self.compute_node.setErrorStatus("TEST123 %s" % self.compute_node.getUid()))
self.assertEqual(self.compute_node._getCachedAccessInfo(),
getExpectedCacheDict(self.compute_node))
self.assertEqual(self.compute_node.getAccessStatus(),
getBaseExpectedDict(self.compute_node))
self.assertEqual(False,
self.compute_node.setErrorStatus("TEST123 %s" % self.compute_node.getUid()))
# Check Software Installation
installation = self.start_requested_software_installation
self.assertEqual(True,
installation.setErrorStatus("TEST123 %s" % installation.getUid()))
self.assertEqual(installation._getCachedAccessInfo(),
getExpectedCacheDict(installation))
self.assertEqual(installation.getAccessStatus(),
getBaseExpectedDict(installation))
self.assertEqual(False,
installation.setErrorStatus("TEST123 %s" % installation.getUid()))
# Compute Partition
partition = self.compute_node.partition1
self.assertEqual(True,
partition.setErrorStatus("TEST123 %s" % partition.getUid()))
self.assertEqual(partition._getCachedAccessInfo(),
getExpectedCacheDict(partition))
self.assertEqual(partition.getAccessStatus(),
getBaseExpectedDict(partition))
self.assertEqual(False,
partition.setErrorStatus("TEST123 %s" % partition.getUid()))
# Software Instance
instance = self.start_requested_software_instance
self.assertEqual(True,
instance.setErrorStatus("TEST123 %s" % instance.getUid()))
self.assertEqual(instance._getCachedAccessInfo(),
getExpectedCacheDict(instance))
self.assertEqual(instance.getAccessStatus(),
getBaseExpectedDict(instance))
self.assertEqual(False,
instance.setErrorStatus("TEST123 %s" % instance.getUid()))
def test_setBuildingStatus(self):
since = rfc1123_date(DateTime())
created_at = since
def getExpectedCacheDict(doc):
return json.dumps({
"user": "ERP5TypeTestCase",
'created_at': '%s' % created_at,
'since': '%s' % since,
'state': "",
"text": "#building TEST123 %s" % doc.getUid()
})
def getBaseExpectedDict(doc):
return {
"user": "ERP5TypeTestCase",
'created_at': '%s' % created_at,
u'since': u'%s' % since,
u'state': u"",
u"text": u"#building TEST123 %s" % doc.getUid(),
'no_data_since_15_minutes': 0,
'no_data_since_5_minutes': 0
}
# Check Software Installation
installation = self.start_requested_software_installation
self.assertEqual(True,
installation.setBuildingStatus("TEST123 %s" % installation.getUid()))
self.assertEqual(installation._getCachedAccessInfo(),
getExpectedCacheDict(installation))
self.assertEqual(installation.getAccessStatus(),
getBaseExpectedDict(installation))
self.assertEqual(False,
installation.setBuildingStatus("TEST123 %s" % installation.getUid()))
class TestSlapOSCloudSoftwareInstance(
SlapOSTestCaseMixin):
def afterSetUp(self):
SlapOSTestCaseMixin.afterSetUp(self)
self._makeTree()
def test_getXmlAsDict(self):
simple_parameter_sample_xml = """<?xml version='1.0' encoding='utf-8'?>
<instance>
<parameter id="p1é">v1é</parameter>
<parameter id="p2é">v2é</parameter>
</instance>
"""
self.assertEqual(
self.software_instance._getXmlAsDict(simple_parameter_sample_xml),
{'p1é': 'v1é', 'p2é': 'v2é'})
def test_getInstanceXmlAsDict(self):
self.software_instance.setTextContent("""<?xml version='1.0' encoding='utf-8'?>
<instance>
<parameter id="p1é">v1é</parameter>
<parameter id="p2é">v2é</parameter>
</instance>
""")
self.assertEqual(
self.software_instance.getInstanceXmlAsDict(),
{'p1é': 'v1é', 'p2é': 'v2é'})
def test_getSlaXmlAsDict(self):
self.software_instance.setSlaXml("""<?xml version='1.0' encoding='utf-8'?>
<instance>
<parameter id="p1é">v1é</parameter>
<parameter id="p2é">v2é</parameter>
</instance>
""")
self.assertEqual(
self.software_instance.getSlaXmlAsDict(),
{'p1é': 'v1é', 'p2é': 'v2é'})
def test_getConnectionXmlAsDict(self):
self.software_instance.setConnectionXml("""<?xml version='1.0' encoding='utf-8'?>
<instance>
<parameter id="p1é">v1é</parameter>
<parameter id="p2é">v2é</parameter>
</instance>
""")
self.assertEqual(
self.software_instance.getConnectionXmlAsDict(),
{'p1é': 'v1é', 'p2é': 'v2é'})
def test_instanceXmlToDict(self):
simple_parameter_sample_xml = """<?xml version='1.0' encoding='utf-8'?>
<instance>
<parameter id="p1é">v1é</parameter>
<parameter id="p2é">v2é</parameter>
</instance>
"""
self.assertEqual(
self.software_instance._instanceXmlToDict(simple_parameter_sample_xml),
# different from getXmlAsDict it don't encode things as utf-8
{u'p1é': u'v1é', u'p2é': u'v2é'})
def test_asParameterDict_not_allocated(self):
self.assertRaises(ValueError,
self.software_instance._asParameterDict)
def test_asParameterDict(self):
self._makeComputeNode()
self._makeComplexComputeNode(with_slave=True)
as_parameter_dict = self.start_requested_software_instance._asParameterDict()
self.assertSameSet(as_parameter_dict.keys(),
['instance_guid', 'instance_title', 'root_instance_title',
'root_instance_short_title', 'xml', 'connection_xml',
'filter_xml', 'slap_computer_id', 'slap_computer_partition_id',
'slap_software_type', 'slap_software_release_url', 'slave_instance_list',
'ip_list', 'full_ip_list', 'timestamp'])
self.assertEqual(as_parameter_dict["instance_guid"],
self.start_requested_software_instance.getReference().decode("UTF-8") )
self.assertEqual(as_parameter_dict["instance_title"],
self.start_requested_software_instance.getTitle().decode("UTF-8") )
self.assertEqual(as_parameter_dict["xml"],
self.start_requested_software_instance.getTextContent() )
self.assertEqual(as_parameter_dict["connection_xml"],
self.start_requested_software_instance.getConnectionXml() )
self.assertEqual(as_parameter_dict["filter_xml"],
self.start_requested_software_instance.getSlaXml() )
self.assertEqual(as_parameter_dict["root_instance_title"],
self.start_requested_software_instance.getSpecialiseTitle().decode("UTF-8"))
self.assertEqual(as_parameter_dict["root_instance_short_title"],
self.start_requested_software_instance.getSpecialiseShortTitle().decode("UTF-8"))
self.assertEqual(as_parameter_dict["slap_computer_id"],
self.compute_node.getReference().decode("UTF-8"))
self.assertEqual(as_parameter_dict["slap_computer_partition_id"],
"partition1")
self.assertEqual(len(as_parameter_dict["slave_instance_list"]), 1)
self.assertSameSet(as_parameter_dict["slave_instance_list"][0].keys(),
['slave_title', 'slap_software_type', 'slave_reference',
'timestamp', 'xml', 'connection_xml'])
self.assertEqual(
self.start_requested_slave_instance.getTitle().decode("UTF-8"),
as_parameter_dict["slave_instance_list"][0]["slave_title"]
)
self.assertEqual(as_parameter_dict["ip_list"], [(u'', u'ip_address_1')])
# Since gateway isn't set both are the same.
self.assertEqual(as_parameter_dict["full_ip_list"], [])
def test_getInstanceTreeIpList(self):
self._makeComputeNode()
self._makeComplexComputeNode(with_slave=True)
self.tic()
self.assertEqual([(u'', u'ip_address_1')],
self.start_requested_software_instance._getInstanceTreeIpList())
class TestSlapOSCloudSlapOSComputeNodeMixin_getCacheComputeNodeInformation(
SlapOSTestCaseMixin):
def afterSetUp(self):
SlapOSTestCaseMixin.afterSetUp(self)
# Prepare compute_node
self.compute_node = self.portal.compute_node_module.template_compute_node\
.Base_createCloneDocument(batch_mode=1)
self.compute_node.edit(
title="Compute Node %s" % self.new_id,
reference="TESTCOMP-%s" % self.new_id
)
if getattr(self, "person", None) is not None:
self.compute_node.edit(
source_administration_value=getattr(self, "person", None),
)
self.compute_node.validate()
self._addERP5Login(self.compute_node)
self.tic()
self.compute_node_id = self.compute_node.getReference()
self.compute_node_user_id = self.compute_node.getUserId()
self.pinDateTime(DateTime())
def beforeTearDown(self):
self.unpinDateTime()
self._cleaupREQUEST()
def test_activate_getCacheComputeNodeInformation_first_access(self):
#
# This is a port of
# TestSlapOSSlapToolgetFullComputerInformation.test_activate_getFullComputerInformation_first_access
#
self._makeComplexComputeNode(with_slave=True)
self.portal.REQUEST['disable_isTestRun'] = True
self.tic()
self.login(self.compute_node_user_id)
user = self.getPortalObject().portal_membership.getAuthenticatedMember().getUserName()
self.compute_node.setAccessStatus(self.compute_node_id)
refresh_etag = self.compute_node._calculateRefreshEtag()
body, etag = self.compute_node._getComputeNodeInformation(user, refresh_etag)
self.commit()
first_etag = self.compute_node._calculateRefreshEtag()
first_body_fingerprint = hashData(
self.compute_node._getCacheComputeNodeInformation(self.compute_node_id)
)
self.assertEqual(first_etag, etag)
self.assertEqual(first_body_fingerprint, hashData(body))
self.assertEqual(0, len(self.portal.portal_activities.getMessageList()))
# Trigger the compute_node reindexation
# This should trigger a new etag, but the body should be the same
self.compute_node.reindexObject()
self.commit()
# Second access
# Check that the result is stable, as the indexation timestamp is not changed yet
current_activity_count = len(self.portal.portal_activities.getMessageList())
self.compute_node.setAccessStatus(self.compute_node_id)
refresh_etag = self.compute_node._calculateRefreshEtag()
body, etag = self.compute_node._getComputeNodeInformation(user, refresh_etag)
self.commit()
self.assertEqual(first_etag, etag)
self.assertEqual(first_body_fingerprint, hashData(body))
self.assertEqual(current_activity_count, len(self.portal.portal_activities.getMessageList()))
self.tic()
# Third access, new calculation expected
# The retrieved informations comes from the cache
# But a new cache modification activity is triggered
self.compute_node.setAccessStatus(self.compute_node_id)
refresh_etag = self.compute_node._calculateRefreshEtag()
body, etag = self.compute_node._getComputeNodeInformation(user, refresh_etag)
self.commit()
second_etag = self.compute_node._calculateRefreshEtag()
second_body_fingerprint = hashData(
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)
self.assertEqual(first_etag, etag)
self.assertEqual(first_body_fingerprint, hashData(body))
self.assertEqual(1, len(self.portal.portal_activities.getMessageList()))
# Execute the cache modification activity
self.tic()
# 4th access
# The new etag value is now used
self.compute_node.setAccessStatus(self.compute_node_id)
refresh_etag = self.compute_node._calculateRefreshEtag()
body, etag = self.compute_node._getComputeNodeInformation(user, refresh_etag)
self.commit()
self.assertEqual(second_etag, etag)
self.assertEqual(first_body_fingerprint, hashData(body))
self.assertEqual(0, len(self.portal.portal_activities.getMessageList()))
# Edit the instance
# This should trigger a new etag and a new body
self.stop_requested_software_instance.edit(text_content=self.generateSafeXml())
self.commit()
# 5th access
# Check that the result is stable, as the indexation timestamp is not changed yet
current_activity_count = len(self.portal.portal_activities.getMessageList())
# Edition does not impact the etag
self.assertEqual(second_etag, self.compute_node._calculateRefreshEtag())
third_body_fingerprint = hashData(
self.compute_node._getCacheComputeNodeInformation(self.compute_node_id)
)
# The edition impacts the response body
self.assertNotEqual(first_body_fingerprint, third_body_fingerprint)
self.compute_node.setAccessStatus(self.compute_node_id)
refresh_etag = self.compute_node._calculateRefreshEtag()
body, etag = self.compute_node._getComputeNodeInformation(user, refresh_etag)
self.commit()
self.assertEqual(second_etag, etag)
self.assertEqual(first_body_fingerprint, hashData(body))
self.assertEqual(current_activity_count, len(self.portal.portal_activities.getMessageList()))
self.tic()
# 6th, the instance edition triggered an interaction workflow
# which updated the cache
self.compute_node.setAccessStatus(self.compute_node_id)
refresh_etag = self.compute_node._calculateRefreshEtag()
body, etag = self.compute_node._getComputeNodeInformation(user, refresh_etag)
self.commit()
third_etag = self.compute_node._calculateRefreshEtag()
self.assertNotEqual(second_etag, third_etag)
self.assertEqual(third_etag, etag)
self.assertEqual(third_body_fingerprint, hashData(body))
self.assertEqual(0, len(self.portal.portal_activities.getMessageList()))
# Remove the slave link to the partition
# Compute Node should loose permission to access the slave instance
self.start_requested_slave_instance.setAggregate('')
self.commit()
# 7th access
# Check that the result is stable, as the indexation timestamp is not changed yet
current_activity_count = len(self.portal.portal_activities.getMessageList())
# Edition does not impact the etag
self.assertEqual(third_etag, self.compute_node._calculateRefreshEtag())
# The edition does not impact the response body yet, as the aggregate relation
# is not yet unindex
self.assertEqual(third_body_fingerprint, hashData(
self.compute_node._getCacheComputeNodeInformation(self.compute_node_id)
))
self.compute_node.setAccessStatus(self.compute_node_id)
refresh_etag = self.compute_node._calculateRefreshEtag()
body, etag = self.compute_node._getComputeNodeInformation(user, refresh_etag)
self.commit()
self.assertEqual(third_etag, etag)
self.assertEqual(third_body_fingerprint, hashData(body))
self.assertEqual(current_activity_count, len(self.portal.portal_activities.getMessageList()))
self.tic()
# 8th access
# changing the aggregate relation trigger the partition reindexation
# which trigger cache modification activity
# So, we should get the correct cached value
self.compute_node.setAccessStatus(self.compute_node_id)
refresh_etag = self.compute_node._calculateRefreshEtag()
body, etag = self.compute_node._getComputeNodeInformation(user, refresh_etag)
self.commit()
fourth_etag = self.compute_node._calculateRefreshEtag()
fourth_body_fingerprint = hashData(
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)
self.assertEqual(fourth_etag, etag)
self.assertEqual(fourth_body_fingerprint, hashData(body))
self.assertEqual(0, len(self.portal.portal_activities.getMessageList()))
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Test Component" module="erp5.portal_type"/>
</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>
</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>test.erp5.testSlapOSCloud</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Test 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">AAAAAAAAAAM=</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>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<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>
......@@ -11,3 +11,5 @@ for compute_partition in [x for x in compute_node.contentValues(portal_type='Com
relative_url=instance.getRelativeUrl(),
reference=instance.getReference(),
comment=state_change.kwargs.get('comment', ''))
compute_node.setErrorStatus('bang')
......@@ -52,6 +52,12 @@
<key> <string>_params</string> </key>
<value> <string>state_change</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>script_ComputeNode_reportBang</string> </value>
......
......@@ -3,6 +3,9 @@ instance = state_change['object']
assert instance.getPortalType() in ["Slave Instance", "Software Instance"]
instance.edit(bang_timestamp=int(DateTime()))
key = "%s_bangstamp" % instance.getReference()
instance.setLastData(key, str(int(instance.getModificationDate())))
comment = state_change.kwargs['comment'] # comment is required to pass the transition
if state_change.kwargs['bang_tree']:
from Products.ZSQLCatalog.SQLCatalog import Query, NegatedQuery
......
......@@ -60,6 +60,12 @@
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>script_RequestedInstance_bangInstanceTree</string> </value>
......
instance = state_change['object']
if instance.getDestinationReference() is not None:
raise ValueError("Certificate still active.")
if instance.getPortalType() != "Software Instance":
# Skip if the instance isn't a Software Instance,
# since Shared Instances cannot find the object.
return
ca = context.getPortalObject().portal_certificate_authority
certificate_dict = ca.getNewCertificate(instance.getReference())
edit_kw = {'destination_reference' : certificate_dict['id'],
'ssl_key' : certificate_dict['key'],
'ssl_certificate': certificate_dict['certificate']
}
instance.edit(**edit_kw)
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Workflow Script" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<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>state_change</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>script_RequestedInstance_generateCertificate</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Workflow Script</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value>
<none/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -29,4 +29,12 @@ if len(request_software_instance_list) == 1:
raise ValueError, "Too many instances '%s' found: %s" % (software_title, [x.path for x in request_software_instance_list])
# Change the title
previous_title = instance.getTitle()
instance.edit(title=software_title, activate_kw={'tag': tag})
# Ensure that the latest date is reset for both new and old instance
hosting = instance.getSpecialise()
for name in [previous_title, software_title]:
# reset request cache
key = '_'.join([hosting, name])
instance.setLastData({}, key=key)
......@@ -52,6 +52,12 @@
<key> <string>_params</string> </key>
<value> <string>state_change</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>script_RequestedInstance_rename</string> </value>
......
instance = state_change['object']
portal = instance.getPortalObject()
if instance.getSslKey() is not None or instance.getSslCertificate() is not None:
instance.edit(ssl_key=None, ssl_certificate=None)
destination_reference = instance.getDestinationReference()
if destination_reference is None:
raise ValueError('No certificate')
try:
portal.portal_certificate_authority\
.revokeCertificate(instance.getDestinationReference())
except ValueError:
# Ignore already revoked certificates, as OpenSSL backend is
# non transactional, so it is ok to allow multiple tries to destruction
# even if certificate was already revoked
pass
instance.setDestinationReference(None)
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Workflow Script" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<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>state_change</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>script_RequestedInstance_revokeCertificate</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Workflow Script</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value>
<none/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -13,3 +13,5 @@ edit_kw = {
instance.edit(**edit_kw)
# Prevent storing broken XML in text content (which prevent to update parameters after)
context.Instance_checkConsistency(state_change)
instance.setLastData(connection_xml)
......@@ -52,6 +52,12 @@
<key> <string>_params</string> </key>
<value> <string>state_change</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>script_RequestedInstance_updateConnectionInformation</string> </value>
......
......@@ -79,15 +79,10 @@ if (request_software_instance is None):
id_group='slap_software_instance_reference',
id_generator='uid')
new_content_kw = {}
if is_slave == True:
software_instance_portal_type = "Slave Instance"
else:
software_instance_portal_type = "Software Instance"
certificate_dict = portal.portal_certificate_authority.getNewCertificate(reference)
new_content_kw['destination_reference'] = certificate_dict['id']
new_content_kw['ssl_key'] = certificate_dict['key']
new_content_kw['ssl_certificate'] = certificate_dict['certificate']
module = portal.getDefaultModule(portal_type="Software Instance")
request_software_instance = module.newContent(
......@@ -95,9 +90,9 @@ if (request_software_instance is None):
title=software_title,
specialise_value=instance_tree,
reference=reference,
activate_kw={'tag': tag},
**new_content_kw
activate_kw={'tag': tag}
)
request_software_instance.generateCertificate()
request_software_instance.validate()
if software_instance_portal_type == "Software Instance":
# Include Certificate Login so Instance become a User
......
......@@ -52,6 +52,12 @@
<key> <string>_params</string> </key>
<value> <string>state_change</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>script_RequesterInstance_request</string> </value>
......
......@@ -17,9 +17,11 @@
<value>
<tuple>
<string>destination/portal_workflow/instance_slap_interface_workflow/transition_bang</string>
<string>destination/portal_workflow/instance_slap_interface_workflow/transition_generate_certificate</string>
<string>destination/portal_workflow/instance_slap_interface_workflow/transition_report_compute_partition_error</string>
<string>destination/portal_workflow/instance_slap_interface_workflow/transition_request_instance</string>
<string>destination/portal_workflow/instance_slap_interface_workflow/transition_request_transfer</string>
<string>destination/portal_workflow/instance_slap_interface_workflow/transition_revoke_certificate</string>
<string>destination/portal_workflow/instance_slap_interface_workflow/transition_unallocate_partition</string>
<string>destination/portal_workflow/instance_slap_interface_workflow/transition_update_connection</string>
</tuple>
......
......@@ -17,9 +17,11 @@
<value>
<tuple>
<string>destination/portal_workflow/instance_slap_interface_workflow/transition_bang</string>
<string>destination/portal_workflow/instance_slap_interface_workflow/transition_generate_certificate</string>
<string>destination/portal_workflow/instance_slap_interface_workflow/transition_request_start</string>
<string>destination/portal_workflow/instance_slap_interface_workflow/transition_request_stop</string>
<string>destination/portal_workflow/instance_slap_interface_workflow/transition_request_transfer</string>
<string>destination/portal_workflow/instance_slap_interface_workflow/transition_revoke_certificate</string>
</tuple>
</value>
</item>
......
......@@ -18,6 +18,7 @@
<tuple>
<string>destination/portal_workflow/instance_slap_interface_workflow/transition_allocate_partition</string>
<string>destination/portal_workflow/instance_slap_interface_workflow/transition_bang</string>
<string>destination/portal_workflow/instance_slap_interface_workflow/transition_generate_certificate</string>
<string>destination/portal_workflow/instance_slap_interface_workflow/transition_rename</string>
<string>destination/portal_workflow/instance_slap_interface_workflow/transition_report_compute_partition_error</string>
<string>destination/portal_workflow/instance_slap_interface_workflow/transition_request_destroy</string>
......@@ -25,6 +26,7 @@
<string>destination/portal_workflow/instance_slap_interface_workflow/transition_request_start</string>
<string>destination/portal_workflow/instance_slap_interface_workflow/transition_request_stop</string>
<string>destination/portal_workflow/instance_slap_interface_workflow/transition_request_transfer</string>
<string>destination/portal_workflow/instance_slap_interface_workflow/transition_revoke_certificate</string>
<string>destination/portal_workflow/instance_slap_interface_workflow/transition_update_connection</string>
</tuple>
</value>
......
......@@ -18,6 +18,7 @@
<tuple>
<string>destination/portal_workflow/instance_slap_interface_workflow/transition_allocate_partition</string>
<string>destination/portal_workflow/instance_slap_interface_workflow/transition_bang</string>
<string>destination/portal_workflow/instance_slap_interface_workflow/transition_generate_certificate</string>
<string>destination/portal_workflow/instance_slap_interface_workflow/transition_rename</string>
<string>destination/portal_workflow/instance_slap_interface_workflow/transition_report_compute_partition_error</string>
<string>destination/portal_workflow/instance_slap_interface_workflow/transition_request_destroy</string>
......@@ -25,6 +26,7 @@
<string>destination/portal_workflow/instance_slap_interface_workflow/transition_request_start</string>
<string>destination/portal_workflow/instance_slap_interface_workflow/transition_request_stop</string>
<string>destination/portal_workflow/instance_slap_interface_workflow/transition_request_transfer</string>
<string>destination/portal_workflow/instance_slap_interface_workflow/transition_revoke_certificate</string>
<string>destination/portal_workflow/instance_slap_interface_workflow/transition_update_connection</string>
</tuple>
</value>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Workflow Transition" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>action_name</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/workflow</string>
<string>after_script/portal_workflow/instance_slap_interface_workflow/script_RequestedInstance_generateCertificate</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>guard_permission</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>transition_generate_certificate</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Workflow Transition</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Generate Certificate</string> </value>
</item>
<item>
<key> <string>trigger_type</string> </key>
<value> <int>2</int> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Workflow Transition" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>action_name</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/workflow</string>
<string>after_script/portal_workflow/instance_slap_interface_workflow/script_RequestedInstance_revokeCertificate</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>guard_permission</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>transition_revoke_certificate</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Workflow Transition</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Revoke Certificate</string> </value>
</item>
<item>
<key> <string>trigger_type</string> </key>
<value> <int>2</int> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
Computer Model | view_capacity
Computer Network | view_compute_node_list
Computer Network | view_software_release
Compute Partition | unfiltered_tracking_list
Compute Node | compute_node_usage
Compute Node | tracking
Compute Node | view_capacity
Compute Partition | unfiltered_tracking_list
Computer Model | view_capacity
Computer Network | view_compute_node_list
Computer Network | view_software_release
Hosting Subscription Module | view
Hosting Subscription | view
Instance Tree Module | view
......
mixin.erp5.SlapOSCacheMixin
mixin.erp5.SlapOSComputeNodeMixin
mixin.erp5.SlapOSComputePartitionMixin
\ No newline at end of file
......@@ -19,6 +19,8 @@ portal_alarms/slapos_stop_collect_instance
portal_alarms/slapos_update_compute_node_capacity_scope
portal_caches/access_status_data_cache_factory
portal_caches/access_status_data_cache_factory/volatile_cache_plugin
portal_caches/compute_node_information_cache_factory
portal_caches/compute_node_information_cache_factory/persistent_cache_plugin
portal_caches/last_stored_data_cache_factory
portal_caches/last_stored_data_cache_factory/volatile_cache_plugin
product_module/compute_node
......
Computer Model | source_administration
Computer Network | source_administration
Compute Node | destination_section
Compute Node | source_administration
Computer Model | source_administration
Computer Network | source_administration
Instance Tree Module | business_application
Instance Tree | destination_section
Slave Instance | aggregate
......
Compute Node | SlapOSCacheMixin
Compute Node | SlapOSComputeNodeMixin
Compute Partition | SlapOSCacheMixin
Compute Partition | SlapOSComputePartitionMixin
Person | SlapOSCacheMixin
\ No newline at end of file
......@@ -14,3 +14,4 @@ test.erp5.testSlapOSCloudUpgrader
test.erp5.testSlapOSCloudShadow
test.erp5.SlapOSTestCaseMixin
test.erp5.SlapOSTestCaseDefaultScenarioMixin
test.erp5.testSlapOSCloud
\ No newline at end of file
......@@ -814,7 +814,7 @@ class TestComputeNode_hasContactedRecently(SlapOSTestCaseMixinWithAbort):
@simulate('ComputeNode_getCreationDate', '*args, **kwargs','return DateTime() - 32')
def test_ComputeNode_hasContactedRecently_memcached(self):
compute_node = self._makeComputeNode()[0]
compute_node.setAccessStatus("#access ")
compute_node.setAccessStatus("")
self.tic()
compute_node.getCreationDate = self.portal.ComputeNode_getCreationDate
......@@ -827,7 +827,7 @@ class TestComputeNode_hasContactedRecently(SlapOSTestCaseMixinWithAbort):
compute_node = self._makeComputeNode()[0]
try:
self.pinDateTime(DateTime()-32)
compute_node.setAccessStatus("#access ")
compute_node.setAccessStatus("")
finally:
self.unpinDateTime()
......@@ -843,7 +843,7 @@ class TestComputeNode_hasContactedRecently(SlapOSTestCaseMixinWithAbort):
compute_node = self._makeComputeNode()[0]
try:
self.pinDateTime(DateTime()-32)
compute_node.setAccessStatus("#access ")
compute_node.setAccessStatus("")
finally:
self.unpinDateTime()
......@@ -1008,7 +1008,7 @@ class TestSlapOSComputeNode_CheckState(TestCRMSkinsMixin):
try:
d = DateTime() - 1.1
self.pinDateTime(d)
compute_node.setAccessStatus("#access ")
compute_node.setAccessStatus("")
finally:
self.unpinDateTime()
......@@ -1045,7 +1045,7 @@ class TestSlapOSComputeNode_CheckState(TestCRMSkinsMixin):
try:
self.pinDateTime(DateTime()-1.1)
compute_node.setAccessStatus("#access ")
compute_node.setAccessStatus("")
finally:
self.unpinDateTime()
......@@ -1275,7 +1275,7 @@ class TestSlapOSHasError(SlapOSTestCaseMixin):
self._makeComputeNode()
self._makeComputePartitionList()
instance.setAccessStatus("#error ")
instance.setErrorStatus("")
self.assertEqual(instance.SoftwareInstance_hasReportedError(), None)
......@@ -1283,7 +1283,7 @@ class TestSlapOSHasError(SlapOSTestCaseMixin):
self.assertEqual(str(instance.SoftwareInstance_hasReportedError()), '#error ')
instance.setAccessStatus("#access ")
instance.setAccessStatus("")
self.assertEqual(instance.SoftwareInstance_hasReportedError(), None)
def test_SoftwareInstallation_hasReportedError(self):
......@@ -1298,14 +1298,14 @@ class TestSlapOSHasError(SlapOSTestCaseMixin):
error_date = DateTime()
try:
self.pinDateTime(error_date)
installation.setAccessStatus("#error ")
installation.setErrorStatus("")
finally:
self.unpinDateTime()
self.assertEqual(
rfc1123_date(installation.SoftwareInstallation_hasReportedError()),
rfc1123_date(error_date))
installation.setAccessStatus("#building ")
installation.setBuildingStatus("")
self.assertEqual(installation.SoftwareInstallation_hasReportedError(), None)
......@@ -1352,7 +1352,7 @@ class TestSlapOSHasError(SlapOSTestCaseMixin):
"slapos-crm-instance-tree-instance-state.notification"),
instance_tree.InstanceTree_checkSoftwareInstanceState())
instance.setAccessStatus("#access ")
instance.setAccessStatus("")
self.assertEqual(None,
instance_tree.InstanceTree_checkSoftwareInstanceState())
......@@ -1455,7 +1455,7 @@ class TestSlapOSHasError(SlapOSTestCaseMixin):
instance.requestInstance(**kw)
self.tic()
instance.setAccessStatus("#access ")
instance.setAccessStatus("")
self.assertEqual(
'Visited by InstanceTree_createSupportRequestEvent %s %s' % \
......@@ -1490,7 +1490,7 @@ class TestSlapOSHasError(SlapOSTestCaseMixin):
self._makeComputePartitionList()
instance.setAggregateValue(self.compute_node.partition1)
instance.setAccessStatus("#error ")
instance.setErrorStatus("")
self.assertEqual(
None,
instance_tree.InstanceTree_checkSoftwareInstanceState())
......
......@@ -17,30 +17,29 @@ def getComputeNodeReferenceAndUserId(item):
compute_node = partition.getParentValue()
if compute_node is not None and compute_node.getValidationState() == 'validated':
return compute_node.getReference(), compute_node.getUserId()
return None, None
return compute_node, compute_node.getReference(), compute_node.getUserId()
return None, None, None
def Item_activateFillComputeNodeInformationCache(state_change):
item = state_change['object']
portal = item.getPortalObject()
compute_node_reference, user_id = getComputeNodeReferenceAndUserId(item)
if compute_node_reference is None:
compute_node, compute_node_reference, user_id = getComputeNodeReferenceAndUserId(item)
if compute_node is None:
return None
if user_id is None:
return None
user = portal.acl_users.getUserById(user_id)
if user is None:
raise ValueError("User %s not found" % user_id)
sm = getSecurityManager()
try:
newSecurityManager(None, user)
portal.portal_slap._activateFillComputeNodeInformationCache(
compute_node_reference, compute_node_reference)
compute_node._activateFillComputeNodeInformationCache(
compute_node_reference)
finally:
setSecurityManager(sm)
......
......@@ -78,16 +78,16 @@ class TestSlapOSSlapToolgetFullComputerInformation(TestSlapOSSlapToolMixin):
self.tic()
self.login(self.compute_node_user_id)
self.portal_slap.getFullComputerInformation(self.compute_node_id)
# First access.
# Cache has been filled by interaction workflow
# (luckily, it seems the cache is filled after everything is indexed)
response = self.portal_slap.getFullComputerInformation(self.compute_node_id)
self.commit()
first_etag = self.portal_slap._calculateRefreshEtag()
first_etag = self.compute_node._calculateRefreshEtag()
first_body_fingerprint = hashData(
self.portal_slap._getCacheComputeNodeInformation(self.compute_node_id,
self.compute_node_id)
self.compute_node._getCacheComputeNodeInformation(self.compute_node_id)
)
self.assertEqual(200, response.status)
self.assertTrue('last-modified' not in response.headers)
......@@ -120,10 +120,9 @@ class TestSlapOSSlapToolgetFullComputerInformation(TestSlapOSSlapToolMixin):
self.commit()
self.assertEqual(200, response.status)
self.assertTrue('last-modified' not in response.headers)
second_etag = self.portal_slap._calculateRefreshEtag()
second_etag = self.compute_node._calculateRefreshEtag()
second_body_fingerprint = hashData(
self.portal_slap._getCacheComputeNodeInformation(self.compute_node_id,
self.compute_node_id)
self.compute_node._getCacheComputeNodeInformation(self.compute_node_id)
)
self.assertNotEqual(first_etag, second_etag)
# The indexation timestamp does not impact the response body
......@@ -154,10 +153,9 @@ class TestSlapOSSlapToolgetFullComputerInformation(TestSlapOSSlapToolMixin):
# Check that the result is stable, as the indexation timestamp is not changed yet
current_activity_count = len(self.portal.portal_activities.getMessageList())
# Edition does not impact the etag
self.assertEqual(second_etag, self.portal_slap._calculateRefreshEtag())
self.assertEqual(second_etag, self.compute_node._calculateRefreshEtag())
third_body_fingerprint = hashData(
self.portal_slap._getCacheComputeNodeInformation(self.compute_node_id,
self.compute_node_id)
self.compute_node._getCacheComputeNodeInformation(self.compute_node_id)
)
# The edition impacts the response body
self.assertNotEqual(first_body_fingerprint, third_body_fingerprint)
......@@ -177,7 +175,7 @@ class TestSlapOSSlapToolgetFullComputerInformation(TestSlapOSSlapToolMixin):
self.commit()
self.assertEqual(200, response.status)
self.assertTrue('last-modified' not in response.headers)
third_etag = self.portal_slap._calculateRefreshEtag()
third_etag = self.compute_node._calculateRefreshEtag()
self.assertNotEqual(second_etag, third_etag)
self.assertEqual(third_etag, response.headers.get('etag'))
self.assertEqual(third_body_fingerprint, hashData(response.body))
......@@ -192,12 +190,11 @@ class TestSlapOSSlapToolgetFullComputerInformation(TestSlapOSSlapToolMixin):
# Check that the result is stable, as the indexation timestamp is not changed yet
current_activity_count = len(self.portal.portal_activities.getMessageList())
# Edition does not impact the etag
self.assertEqual(third_etag, self.portal_slap._calculateRefreshEtag())
self.assertEqual(third_etag, self.compute_node._calculateRefreshEtag())
# 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._getCacheComputeNodeInformation(self.compute_node_id,
self.compute_node_id)
self.compute_node._getCacheComputeNodeInformation(self.compute_node_id)
))
response = self.portal_slap.getFullComputerInformation(self.compute_node_id)
self.commit()
......@@ -217,10 +214,9 @@ class TestSlapOSSlapToolgetFullComputerInformation(TestSlapOSSlapToolMixin):
self.commit()
self.assertEqual(200, response.status)
self.assertTrue('last-modified' not in response.headers)
fourth_etag = self.portal_slap._calculateRefreshEtag()
fourth_etag = self.compute_node._calculateRefreshEtag()
fourth_body_fingerprint = hashData(
self.portal_slap._getCacheComputeNodeInformation(self.compute_node_id,
self.compute_node_id)
self.compute_node._getCacheComputeNodeInformation(self.compute_node_id)
)
self.assertNotEqual(third_etag, fourth_etag)
# The indexation timestamp does not impact the response body
......@@ -733,43 +729,9 @@ class TestSlapOSSlapToolComputeNodeAccess(TestSlapOSSlapToolMixin):
response = self.portal_slap.computerBang(self.compute_node_id,
error_log)
self.assertEqual('None', response)
created_at = rfc1123_date(DateTime())
since = created_at
response = self.portal_slap.getComputerStatus(self.compute_node_id)
# check returned XML
xml_fp = StringIO.StringIO()
# We do not assert getComputerStatus on this test, since
# the change of the timestamp is part of reportComputeNodeBang
xml.dom.ext.PrettyPrint(xml.dom.ext.reader.Sax.FromXml(response.body),
stream=xml_fp)
xml_fp.seek(0)
got_xml = xml_fp.read()
expected_xml = """\
<?xml version='1.0' encoding='UTF-8'?>
<marshal>
<dictionary id='i2'>
<string>created_at</string>
<string>%(created_at)s</string>
<string>no_data_since_15_minutes</string>
<int>0</int>
<string>no_data_since_5_minutes</string>
<int>0</int>
<string>since</string>
<string>%(since)s</string>
<string>state</string>
<string/>
<string>text</string>
<string>#error bang</string>
<string>user</string>
<string>%(compute_node_id)s</string>
</dictionary>
</marshal>
""" % dict(
created_at=created_at,
since=since,
compute_node_id=self.compute_node_id,
)
self.assertEqual(expected_xml, got_xml,
'\n'.join([q for q in difflib.unified_diff(expected_xml.split('\n'), got_xml.split('\n'))]))
self.assertComputeNodeBangSimulator((), {'comment': error_log})
finally:
if os.path.exists(self.compute_node_bang_simulator):
......@@ -2635,43 +2597,9 @@ class TestSlapOSSlapToolPersonAccess(TestSlapOSSlapToolMixin):
response = self.portal_slap.computerBang(self.compute_node_id,
error_log)
self.assertEqual('None', response)
created_at = rfc1123_date(DateTime())
since = created_at
response = self.portal_slap.getComputerStatus(self.compute_node_id)
# check returned XML
xml_fp = StringIO.StringIO()
# We do not assert getComputerStatus on this test, since
# the change of the timestamp is part of reportComputeNodeBang
xml.dom.ext.PrettyPrint(xml.dom.ext.reader.Sax.FromXml(response.body),
stream=xml_fp)
xml_fp.seek(0)
got_xml = xml_fp.read()
expected_xml = """\
<?xml version='1.0' encoding='UTF-8'?>
<marshal>
<dictionary id='i2'>
<string>created_at</string>
<string>%(created_at)s</string>
<string>no_data_since_15_minutes</string>
<int>0</int>
<string>no_data_since_5_minutes</string>
<int>0</int>
<string>since</string>
<string>%(since)s</string>
<string>state</string>
<string/>
<string>text</string>
<string>#error bang</string>
<string>user</string>
<string>%(person_reference)s</string>
</dictionary>
</marshal>
""" % dict(
created_at=created_at,
since=since,
person_reference=self.person_reference,
)
self.assertEqual(expected_xml, got_xml,
'\n'.join([q for q in difflib.unified_diff(expected_xml.split('\n'), got_xml.split('\n'))]))
self.assertComputeNodeBangSimulator((), {'comment': error_log})
finally:
if os.path.exists(self.compute_node_bang_simulator):
......
......@@ -38,37 +38,20 @@ from Products.DCWorkflow.DCWorkflow import ValidationFailed
from Products.ERP5Type.Globals import InitializeClass
from Products.ERP5Type.Tool.BaseTool import BaseTool
from Products.ERP5Type import Permissions
from Products.ERP5Type.Cache import DEFAULT_CACHE_SCOPE
from Products.ERP5Type.Cache import CachingMethod
from lxml import etree
import time
from Products.ERP5Type.tests.utils import DummyMailHostMixin
try:
from slapos.slap.slap import (
Computer as ComputeNode,
ComputerPartition as SlapComputePartition,
SoftwareInstance,
SoftwareRelease)
from slapos.util import dict2xml, xml2dict, calculate_dict_hash, loads, dumps
Computer as ComputeNode)
from slapos.util import dict2xml, calculate_dict_hash, loads, dumps
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 SoftwareInstance:
def __init__(self):
raise ImportError
class SoftwareRelease:
def __init__(self):
raise ImportError
def dict2xml(dictionary):
raise ImportError
def xml2dict(dictionary):
raise ImportError
def calculate_dict_hash(dictionary):
raise ImportError
def loads(*args):
......@@ -118,6 +101,15 @@ def convertToREST(function):
wrapper.__doc__ = function.__doc__
return wrapper
def castToStr(dict_kw):
instance = etree.Element('instance')
for _id, _value in dict_kw.iteritems():
# cast everything to string
etree.SubElement(instance, "parameter",
attrib={'id':_id}).text = str(_value)
return etree.tostring(instance, pretty_print=True,
xml_declaration=True, encoding='utf-8')
def _assertACI(document):
sm = getSecurityManager()
......@@ -162,194 +154,6 @@ class SlapTool(BaseTool):
# Public GET methods
####################################################
def _isTestRun(self):
if self.REQUEST.get('disable_isTestRun', False):
return False
if issubclass(self.getPortalObject().MailHost.__class__, DummyMailHostMixin) \
or self.REQUEST.get('test_list'):
return True
return False
def _getCachePlugin(self):
return self.getPortalObject().portal_caches\
.getRamCacheRoot().get('compute_node_information_cache_factory')\
.getCachePluginList()[0]
def _getCacheComputeNodeInformation(self, compute_node_id, user):
self.REQUEST.response.setHeader('Content-Type', 'text/xml; charset=utf-8')
slap_compute_node = ComputeNode(compute_node_id.decode("UTF-8"))
parent_uid = self._getComputeNodeUidByReference(compute_node_id)
slap_compute_node._computer_partition_list = []
slap_compute_node._software_release_list = \
self._getSoftwareReleaseValueListForComputeNode(compute_node_id)
unrestrictedSearchResults = self.getPortalObject().portal_catalog.unrestrictedSearchResults
compute_partition_list = unrestrictedSearchResults(
parent_uid=parent_uid,
validation_state="validated",
portal_type="Compute Partition"
)
self._calculateSlapComputeNodeInformation(slap_compute_node, compute_partition_list)
return dumps(slap_compute_node)
def _fillComputeNodeInformationCache(self, compute_node_id, user):
key = '%s_%s' % (compute_node_id, user)
try:
self._getCachePlugin().set(key, DEFAULT_CACHE_SCOPE,
dict (
time=time.time(),
refresh_etag=self._calculateRefreshEtag(),
data=self._getCacheComputeNodeInformation(compute_node_id, user),
),
cache_duration=self.getPortalObject().portal_caches\
.getRamCacheRoot().get('compute_node_information_cache_factory'\
).cache_duration
)
except (Unauthorized, IndexError):
# XXX: Unauthorized hack. Race condition of not ready setup delivery which provides
# security information shall not make this method fail, as it will be
# called later anyway
# Note: IndexError ignored, as it happend in case if full reindex is
# called on site
pass
def _activateFillComputeNodeInformationCache(self, compute_node_id, user):
tag = 'compute_node_information_cache_fill_%s_%s' % (compute_node_id, user)
if self.getPortalObject().portal_activities.countMessageWithTag(tag) == 0:
self.activate(activity='SQLQueue', tag=tag)._fillComputeNodeInformationCache(
compute_node_id, user)
def _calculateSlapComputeNodeInformation(self, slap_compute_node, compute_partition_list):
if len(compute_partition_list) == 0:
return
unrestrictedSearchResults = self.getPortalObject().portal_catalog.unrestrictedSearchResults
compute_partition_uid_list = [x.uid for x in compute_partition_list]
grouped_software_instance_list = unrestrictedSearchResults(
portal_type="Software Instance",
default_aggregate_uid=compute_partition_uid_list,
validation_state="validated",
group_by_list=['default_aggregate_uid'],
select_list=['default_aggregate_uid', 'count(*)']
)
slave_software_instance_list = unrestrictedSearchResults(
default_aggregate_uid=compute_partition_uid_list,
portal_type='Slave Instance',
validation_state="validated",
select_list=['default_aggregate_uid'],
**{"slapos_item.slap_state": "start_requested"}
)
for compute_partition in compute_partition_list:
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(
self._getSlapPartitionByPackingList(
_assertACI(compute_partition.getObject()),
software_instance_list,
[x for x in slave_software_instance_list if (x.default_aggregate_uid == compute_partition.getUid())]
)
)
def _calculateRefreshEtag(self):
# check max indexation timestamp
# it is unlikely to get an empty catalog
last_indexed_entry = self.getPortalObject().portal_catalog(
select_list=['indexation_timestamp'],
portal_type=['Compute Node', 'Compute Partition',
'Software Instance', 'Slave Instance',
'Software Installation'],
sort_on=[('indexation_timestamp', 'DESC')],
limit=1,
)[0]
return '%s_%s' % (last_indexed_entry.uid,
last_indexed_entry.indexation_timestamp)
def _getComputeNodeInformation(self, compute_node_id, user, refresh_etag):
portal = self.getPortalObject()
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(compute_node_id.decode("UTF-8"))
parent_uid = self._getComputeNodeUidByReference(compute_node_id)
slap_compute_node._computer_partition_list = []
if user_type in ('Compute Node', 'Person'):
if not self._isTestRun():
cache_plugin = self._getCachePlugin()
key = '%s_%s' % (compute_node_id, 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
self._activateFillComputeNodeInformationCache(compute_node_id, user)
return cached_dict['data'], cached_etag
else:
self._activateFillComputeNodeInformationCache(compute_node_id, user)
self.REQUEST.response.setStatus(503)
return self.REQUEST.response, None
else:
return self._getCacheComputeNodeInformation(compute_node_id, user), None
else:
slap_compute_node._software_release_list = []
if user_type == 'Software Instance':
compute_node = self.getPortalObject().portal_catalog.unrestrictedSearchResults(
portal_type='Compute Node', reference=compute_node_id,
validation_state="validated")[0].getObject()
compute_partition_list = compute_node.contentValues(
portal_type="Compute Partition",
checked_permission="View")
else:
compute_partition_list = self.getPortalObject().portal_catalog.unrestrictedSearchResults(
parent_uid=parent_uid,
validation_state="validated",
portal_type="Compute Partition")
self._calculateSlapComputeNodeInformation(slap_compute_node, compute_partition_list)
return dumps(slap_compute_node), None
@UnrestrictedMethod
def _getInstanceTreeIpList(self, compute_node_id, compute_partition_id):
software_instance = self._getSoftwareInstanceForComputePartition(
compute_node_id, compute_partition_id)
if software_instance is None or \
software_instance.getSlapState() == 'destroy_requested':
return dumps([])
# Search instance tree
hosting = software_instance.getSpecialiseValue()
while hosting and hosting.getPortalType() != "Instance Tree":
hosting = hosting.getSpecialiseValue()
ip_address_list = []
for instance in hosting.getSpecialiseRelatedValueList(
portal_type="Software Instance"):
compute_partition = instance.getAggregateValue(portal_type="Compute Partition")
if not compute_partition:
continue
for internet_protocol_address in compute_partition.contentValues(
portal_type='Internet Protocol Address'):
ip_address_list.append(
(internet_protocol_address.getNetworkInterface('').decode("UTF-8"),
internet_protocol_address.getIpAddress().decode("UTF-8"))
)
return dumps(ip_address_list)
security.declareProtected(Permissions.AccessContentsInformation,
'getFullComputerInformation')
def getFullComputerInformation(self, computer_id):
......@@ -362,9 +166,16 @@ class SlapTool(BaseTool):
user = self.getPortalObject().portal_membership.getAuthenticatedMember().getUserName()
if str(user) == computer_id:
compute_node = self.getPortalObject().portal_membership.getAuthenticatedMember().getUserValue()
compute_node.setAccessStatus('#access %s' % computer_id)
refresh_etag = self._calculateRefreshEtag()
body, etag = self._getComputeNodeInformation(computer_id, user, refresh_etag)
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(
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)
if self.REQUEST.response.getStatus() == 200:
# Keep in cache server for 7 days
......@@ -386,8 +197,12 @@ class SlapTool(BaseTool):
Search and return all Compute Partition IP address related to one
Instance Tree
"""
result = self._getInstanceTreeIpList(computer_id,
computer_partition_id)
software_instance = self._getSoftwareInstanceForComputePartition(
computer_id, computer_partition_id)
if software_instance is not None:
result = software_instance._getInstanceTreeIpList()
else:
result = []
if self.REQUEST.response.getStatus() == 200:
# Keep in cache server for 7 days
......@@ -396,7 +211,7 @@ class SlapTool(BaseTool):
self.REQUEST.response.setHeader('Vary',
'REMOTE_USER')
self.REQUEST.response.setHeader('Last-Modified', rfc1123_date(DateTime()))
self.REQUEST.response.setBody(result)
self.REQUEST.response.setBody(dumps(result))
return self.REQUEST.response
else:
return result
......@@ -443,15 +258,13 @@ class SlapTool(BaseTool):
else:
data_dict = instance.getAccessStatus()
last_modified = rfc1123_date(DateTime())
# Keep in cache server for 7 days
self.REQUEST.response.setStatus(200)
self.REQUEST.response.setHeader('Cache-Control',
'public, max-age=60, stale-if-error=604800')
self.REQUEST.response.setHeader('Vary',
'REMOTE_USER')
self.REQUEST.response.setHeader('Last-Modified', last_modified)
self.REQUEST.response.setHeader('Last-Modified', rfc1123_date(DateTime()))
self.REQUEST.response.setHeader('Content-Type', 'text/xml; charset=utf-8')
self.REQUEST.response.setBody(dumps(data_dict))
return self.REQUEST.response
......@@ -462,22 +275,16 @@ class SlapTool(BaseTool):
"""
Get the connection status of the partition
"""
compute_node = self.getPortalObject().portal_catalog.unrestrictedSearchResults(
portal_type='Compute Node', reference=computer_id,
validation_state="validated")[0].getObject()
# Be sure to prevent accessing information to disallowed users
compute_node = _assertACI(compute_node)
compute_node = self._getComputeNodeDocument(computer_id)
data_dict = compute_node.getAccessStatus()
last_modified = rfc1123_date(DateTime())
# Keep in cache server for 7 days
self.REQUEST.response.setStatus(200)
self.REQUEST.response.setHeader('Cache-Control',
'public, max-age=60, stale-if-error=604800')
self.REQUEST.response.setHeader('Vary',
'REMOTE_USER')
self.REQUEST.response.setHeader('Last-Modified', last_modified)
self.REQUEST.response.setHeader('Last-Modified', rfc1123_date(DateTime()))
self.REQUEST.response.setHeader('Content-Type', 'text/xml; charset=utf-8')
self.REQUEST.response.setBody(dumps(data_dict))
return self.REQUEST.response
......@@ -488,15 +295,11 @@ class SlapTool(BaseTool):
"""
Get the connection status of the software installation
"""
compute_node = self.getPortalObject().portal_catalog.unrestrictedSearchResults(
portal_type='Compute Node', reference=computer_id,
validation_state="validated")[0].getObject()
compute_node = self._getComputeNodeDocument(computer_id)
# Be sure to prevent accessing information to disallowed users
compute_node = _assertACI(compute_node)
try:
software_installation = self._getSoftwareInstallationForComputeNode(
url,
compute_node)
software_installation = compute_node._getSoftwareInstallationFromUrl(url)
except NotFound:
data_dict = self._getAccessStatus(None)
else:
......@@ -793,7 +596,6 @@ class SlapTool(BaseTool):
Fire up bung on Compute Node
"""
compute_node = self._getComputeNodeDocument(compute_node_id)
compute_node.setAccessStatus('#error bang')
return compute_node.reportComputeNodeBang(comment=message)
security.declareProtected(Permissions.AccessContentsInformation,
......@@ -813,20 +615,6 @@ class SlapTool(BaseTool):
compute_node.ComputeNode_updateFromDict(compute_node_dict)
return 'Content properly posted.'
security.declareProtected(Permissions.AccessContentsInformation,
'useComputerPartition')
def useComputerPartition(self, computer_id, computer_partition_id,
use_string):
"""Warning : deprecated method."""
compute_node_document = self._getComputeNodeDocument(computer_id)
compute_partition_document = self._getComputePartitionDocument(
compute_node_document.getReference(), computer_partition_id)
# easy way to start to store usage messages sent by client in related Web
# Page text_content...
self._reportUsage(compute_partition_document, use_string)
return """Content properly posted.
WARNING : this method is deprecated. Please use useComputer."""
@convertToREST
def _generateComputerCertificate(self, compute_node_id):
self._getComputeNodeDocument(compute_node_id).generateCertificate()
......@@ -862,71 +650,10 @@ class SlapTool(BaseTool):
"""
# Try to get the compute partition to raise an exception if it doesn't
# exist
portal = self.getPortalObject()
compute_partition_document = self._getComputePartitionDocument(
computer_reference, computer_partition_reference)
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
if compute_partition_document.getSlapState() == 'busy':
software_instance_list = portal.portal_catalog.unrestrictedSearchResults(
portal_type="Software Instance",
default_aggregate_uid=compute_partition_document.getUid(),
validation_state="validated",
limit=2,
)
software_instance_count = len(software_instance_list)
if software_instance_count == 1:
software_instance = _assertACI(software_instance_list[0].getObject())
elif software_instance_count > 1:
# XXX do not prevent the system to work if one partition is broken
raise NotImplementedError, "Too many instances %s linked to %s" % \
([x.path for x in software_instance_list],
compute_partition_document.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'
if state == "start_requested":
slap_partition._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
parameter_dict = self._getSoftwareInstanceAsParameterDict(
software_instance)
# software instance has to define an xml parameter
slap_partition._parameter_dict = self._instanceXmlToDict(
parameter_dict.pop('xml'))
slap_partition._connection_dict = self._instanceXmlToDict(
parameter_dict.pop('connection_xml'))
slap_partition._filter_dict = self._instanceXmlToDict(
parameter_dict.pop('filter_xml'))
slap_partition._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 = self._instanceXmlToDict(
slave_instance_dict.pop("connection_xml"))
slave_instance_dict.update(connection_dict)
slave_instance_dict['connection-parameter-hash'] = \
calculate_dict_hash(connection_dict)
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)
result = dumps(slap_partition)
slap_compute_partition = compute_partition_document._registerComputePartition()
# Keep in cache server for 7 days
self.REQUEST.response.setStatus(200)
......@@ -936,7 +663,7 @@ class SlapTool(BaseTool):
'REMOTE_USER')
self.REQUEST.response.setHeader('Last-Modified', rfc1123_date(DateTime()))
self.REQUEST.response.setHeader('Content-Type', 'text/xml; charset=utf-8')
self.REQUEST.response.setBody(result)
self.REQUEST.response.setBody(dumps(slap_compute_partition))
return self.REQUEST.response
......@@ -966,77 +693,6 @@ class SlapTool(BaseTool):
return False
def _instanceXmlToDict(self, xml):
result_dict = {}
try:
result_dict = xml2dict(xml)
except (etree.XMLSchemaError, etree.XMLSchemaParseError, # pylint: disable=catching-non-exception
etree.XMLSchemaValidateError, etree.XMLSyntaxError): # pylint: disable=catching-non-exception
LOG('SlapTool', INFO, 'Issue during parsing xml:', error=True)
return result_dict
def _getSlapPartitionByPackingList(self, compute_partition_document,
software_instance_list,
slave_instance_sql_list):
compute_node = compute_partition_document
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
software_instance = None
if compute_partition_document.getSlapState() == 'busy':
software_instance_count = len(software_instance_list)
if software_instance_count == 1:
software_instance = _assertACI(software_instance_list[0].getObject())
elif software_instance_count > 1:
# XXX do not prevent the system to work if one partition is broken
raise NotImplementedError, "Too many instances linked to %s" % \
compute_partition_document.getRelativeUrl()
if software_instance is not None:
state = software_instance.getSlapState()
if state == "stop_requested":
slap_partition._requested_state = 'stopped'
if state == "start_requested":
slap_partition._requested_state = 'started'
slap_partition._access_status = software_instance.getTextAccessStatus()
slap_partition._software_release_document = SoftwareRelease(
software_release=software_instance.getUrlString().decode("UTF-8"),
computer_guid=compute_node_id)
slap_partition._need_modification = 1
parameter_dict = self._getSoftwareInstanceAsParameterDict(
software_instance,
slave_instance_sql_list=slave_instance_sql_list
)
# software instance has to define an xml parameter
slap_partition._parameter_dict = self._instanceXmlToDict(
parameter_dict.pop('xml'))
slap_partition._connection_dict = self._instanceXmlToDict(
parameter_dict.pop('connection_xml'))
slap_partition._filter_dict = self._instanceXmlToDict(
parameter_dict.pop('filter_xml'))
slap_partition._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(
slave_instance_dict.pop("connection_xml")))
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)
return slap_partition
@convertToREST
def _supplySupply(self, url, compute_node_id, state):
"""
......@@ -1050,31 +706,28 @@ class SlapTool(BaseTool):
"""
Log the software release status
"""
compute_node_document = self._getComputeNodeDocument(compute_node_id)
software_installation = self._getSoftwareInstallationForComputeNode(url,
compute_node_document)
software_installation.setAccessStatus(
'#building software release %s' % url, "building")
compute_node = self._getComputeNodeDocument(compute_node_id)
software_installation = compute_node._getSoftwareInstallationFromUrl(url)
software_installation.setBuildingStatus(
'software release %s' % url, "building")
@convertToREST
def _availableSoftwareRelease(self, url, compute_node_id):
"""
Log the software release status
"""
compute_node_document = self._getComputeNodeDocument(compute_node_id)
software_installation = self._getSoftwareInstallationForComputeNode(url,
compute_node_document)
compute_node = self._getComputeNodeDocument(compute_node_id)
software_installation = compute_node._getSoftwareInstallationFromUrl(url)
software_installation.setAccessStatus(
'#access software release %s available' % url, "available")
'software release %s available' % url, "available")
@convertToREST
def _destroyedSoftwareRelease(self, url, compute_node_id):
"""
Reports that Software Release is destroyed
"""
compute_node_document = self._getComputeNodeDocument(compute_node_id)
software_installation = self._getSoftwareInstallationForComputeNode(url,
compute_node_document)
compute_node = self._getComputeNodeDocument(compute_node_id)
software_installation = compute_node._getSoftwareInstallationFromUrl(url)
if software_installation.getSlapState() != 'destroy_requested':
raise NotFound
if self.getPortalObject().portal_workflow.isTransitionPossible(software_installation,
......@@ -1088,18 +741,14 @@ class SlapTool(BaseTool):
"""
Add an error for the software Instance Workflow
"""
if error_log is None:
error_log = ""
instance = self._getSoftwareInstanceForComputePartition(
compute_node_id,
compute_partition_id)
status_changed = instance.setAccessStatus(
'#error while instanciating: %s' % error_log[-80:])
if status_changed:
instance.reindexObject()
if error_log is None:
error_log = ""
instance.setErrorStatus(
'while instanciating: %s' % error_log[-80:], reindex=1)
@convertToREST
def _softwareInstanceRename(self, new_name, compute_node_id,
......@@ -1107,11 +756,7 @@ class SlapTool(BaseTool):
software_instance = self._getSoftwareInstanceForComputePartition(
compute_node_id, compute_partition_id,
slave_reference)
hosting = software_instance.getSpecialise()
for name in [software_instance.getTitle(), new_name]:
# reset request cache
key = '_'.join([hosting, name])
software_instance.setLastData({}, key=key)
return software_instance.rename(new_name=new_name,
comment="Rename %s into %s" % (software_instance.title, new_name))
......@@ -1126,16 +771,12 @@ class SlapTool(BaseTool):
compute_node_id,
compute_partition_id)
software_instance.setAccessStatus('#error bang called')
software_instance.setErrorStatus('bang called')
timestamp = str(int(software_instance.getModificationDate()))
key = "%s_bangstamp" % software_instance.getReference()
self.getPortalObject().portal_workflow.getInfoFor(
software_instance, 'action', wf_id='instance_slap_interface_workflow')
if (software_instance.getLastData(key) != timestamp):
if not software_instance.isLastData(key, timestamp):
software_instance.bang(bang_tree=True, comment=message)
software_instance.setLastData(key, str(int(software_instance.getModificationDate())))
return "OK"
@convertToREST
......@@ -1146,9 +787,9 @@ class SlapTool(BaseTool):
instance = self._getSoftwareInstanceForComputePartition(
compute_node_id,
compute_partition_id)
status_changed = instance.setAccessStatus('#access Instance correctly started', "started")
if status_changed:
instance.reindexObject()
instance.setAccessStatus(
'Instance correctly started', "started", reindex=1)
@convertToREST
def _stoppedComputePartition(self, compute_node_id, compute_partition_id):
......@@ -1158,9 +799,8 @@ class SlapTool(BaseTool):
instance = self._getSoftwareInstanceForComputePartition(
compute_node_id,
compute_partition_id)
status_changed = instance.setAccessStatus('#access Instance correctly stopped', "stopped")
if status_changed:
instance.reindexObject()
instance.setAccessStatus(
'Instance correctly stopped', "stopped", reindex=1)
@convertToREST
def _destroyedComputePartition(self, compute_node_id, compute_partition_id):
......@@ -1170,30 +810,13 @@ class SlapTool(BaseTool):
instance = self._getSoftwareInstanceForComputePartition(
compute_node_id,
compute_partition_id)
if instance.getSlapState() == 'destroy_requested':
# remove certificate from SI
if instance.getSslKey() is not None or instance.getSslCertificate() is not None:
instance.edit(
ssl_key=None,
ssl_certificate=None,
)
instance.revokeCertificate()
if instance.getValidationState() == 'validated':
instance.invalidate()
# XXX Integrate with REST API
# Code duplication will be needed until SlapTool is removed
# revoke certificate
portal = self.getPortalObject()
try:
portal.portal_certificate_authority\
.revokeCertificate(instance.getDestinationReference())
except ValueError:
# Ignore already revoked certificates, as OpenSSL backend is
# non transactional, so it is ok to allow multiple tries to destruction
# even if certificate was already revoked
pass
@convertToREST
def _setComputePartitionConnectionXml(self, compute_node_id,
compute_partition_id,
......@@ -1207,11 +830,11 @@ class SlapTool(BaseTool):
compute_partition_id,
slave_reference)
connection_xml = dict2xml(loads(connection_xml))
if software_instance.getLastData() != connection_xml:
if not software_instance.isLastData(value=connection_xml):
software_instance.updateConnection(
connection_xml=connection_xml,
)
software_instance.setLastData(connection_xml)
@convertToREST
def _requestComputePartition(self, compute_node_id, compute_partition_id,
......@@ -1248,85 +871,51 @@ class SlapTool(BaseTool):
else:
filter_kw = dict()
instance = etree.Element('instance')
for parameter_id, parameter_value in partition_parameter_kw.iteritems():
# cast everything to string
parameter_value = str(parameter_value)
etree.SubElement(instance, "parameter",
attrib={'id':parameter_id}).text = parameter_value
instance_xml = etree.tostring(instance, pretty_print=True,
xml_declaration=True, encoding='utf-8')
instance = etree.Element('instance')
for parameter_id, parameter_value in filter_kw.iteritems():
# cast everything to string
parameter_value = str(parameter_value)
etree.SubElement(instance, "parameter",
attrib={'id':parameter_id}).text = parameter_value
sla_xml = etree.tostring(instance, pretty_print=True,
xml_declaration=True, encoding='utf-8')
kw = dict(software_release=software_release,
software_type=software_type,
software_title=partition_reference,
instance_xml=castToStr(partition_parameter_kw),
shared=shared,
sla_xml=castToStr(filter_kw),
state=state)
portal = self.getPortalObject()
if compute_node_id and compute_partition_id:
# requested by Software Instance, there is already top part of tree
software_instance_document = self.\
requester = self.\
_getSoftwareInstanceForComputePartition(compute_node_id,
compute_partition_id)
instance_tree = software_instance_document.getSpecialiseValue()
instance_tree = requester.getSpecialiseValue()
if instance_tree is not None and instance_tree.getSlapState() == "stop_requested":
state = 'stopped'
kw = dict(software_release=software_release,
software_type=software_type,
software_title=partition_reference,
instance_xml=instance_xml,
shared=shared,
sla_xml=sla_xml,
state=state)
key = '_'.join([software_instance_document.getSpecialise(), partition_reference])
value = dict(
hash='_'.join([software_instance_document.getRelativeUrl(), str(kw)]),
)
last_data = software_instance_document.getLastData(key)
requested_software_instance = None
if last_data is not None and isinstance(last_data, dict):
requested_software_instance = portal.restrictedTraverse(
last_data.get('request_instance'), None)
if last_data is None or not isinstance(last_data, type(value)) or \
last_data.get('hash') != value['hash'] or \
requested_software_instance is None:
software_instance_document.requestInstance(**kw)
requested_software_instance = self.REQUEST.get('request_instance')
if requested_software_instance is not None:
value['request_instance'] = requested_software_instance\
.getRelativeUrl()
software_instance_document.setLastData(value, key=key)
kw['state'] = 'stopped'
key = '_'.join([instance_tree.getRelativeUrl(), partition_reference])
else:
# requested as root, so done by human
person = portal.portal_membership.getAuthenticatedMember().getUserValue()
kw = dict(software_release=software_release,
software_type=software_type,
software_title=partition_reference,
shared=shared,
instance_xml=instance_xml,
sla_xml=sla_xml,
state=state)
key = '_'.join([person.getRelativeUrl(), partition_reference])
requester = portal.portal_membership.getAuthenticatedMember().getUserValue()
key = '_'.join([requester.getRelativeUrl(), partition_reference])
last_data = requester.getLastData(key)
requested_software_instance = None
value = dict(
hash=str(kw)
hash='_'.join([requester.getRelativeUrl(), str(kw)]),
)
last_data = person.getLastData(key)
if last_data is not None and isinstance(last_data, dict):
requested_software_instance = portal.restrictedTraverse(
if last_data is not None and isinstance(last_data, type(value)):
requested_software_instance = self.restrictedTraverse(
last_data.get('request_instance'), None)
if last_data is None or not isinstance(last_data, type(value)) or \
last_data.get('hash') != value['hash'] or \
requested_software_instance is None:
person.requestSoftwareInstance(**kw)
if compute_node_id and compute_partition_id:
requester.requestInstance(**kw)
else:
# requester is a person so we use another method
requester.requestSoftwareInstance(**kw)
requested_software_instance = self.REQUEST.get('request_instance')
if requested_software_instance is not None:
value['request_instance'] = requested_software_instance\
.getRelativeUrl()
requested_software_instance.setLastData(value, key=key)
requester.setLastData(value, key=key)
if requested_software_instance is None:
raise SoftwareInstanceNotReady
......@@ -1334,24 +923,7 @@ class SlapTool(BaseTool):
if not requested_software_instance.getAggregate(portal_type="Compute Partition"):
raise SoftwareInstanceNotReady
else:
parameter_dict = self._getSoftwareInstanceAsParameterDict(requested_software_instance)
# software instance has to define an xml parameter
xml = self._instanceXmlToDict(
parameter_dict.pop('xml'))
connection_xml = self._instanceXmlToDict(
parameter_dict.pop('connection_xml'))
filter_xml = self._instanceXmlToDict(
parameter_dict.pop('filter_xml'))
instance_guid = parameter_dict.pop('instance_guid')
software_instance = SoftwareInstance(**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 dumps(software_instance)
return dumps(requested_software_instance._asSoftwareInstance())
@UnrestrictedMethod
def _updateComputePartitionRelatedInstanceList(self, compute_node_id,
......@@ -1369,26 +941,7 @@ class SlapTool(BaseTool):
_getSoftwareInstanceForComputePartition(compute_node_id,
compute_partition_id)
cache_reference = '%s-PREDLIST' % software_instance_document.getReference()
if software_instance_document.getLastData(cache_reference) != instance_reference_xml:
instance_reference_list = loads(instance_reference_xml)
current_successor_list = software_instance_document.getSuccessorValueList(
portal_type=['Software Instance', 'Slave Instance'])
current_successor_title_list = [i.getTitle() for i in
current_successor_list]
# If there are items to remove
if list(set(current_successor_title_list).difference(instance_reference_list)) != []:
successor_list = [instance.getRelativeUrl() for instance in
current_successor_list if instance.getTitle()
in instance_reference_list]
LOG('SlapTool', INFO, '%s, %s: Updating successor list to %s' % (
compute_node_id, compute_partition_id, successor_list), error=False)
software_instance_document.edit(successor_list=successor_list,
comment='successor_list edited to unlink non commited instances')
software_instance_document.setLastData(instance_reference_xml, key=cache_reference)
software_instance_document._updateSucessorList(instance_reference_xml)
####################################################
# Internals methods
......@@ -1446,31 +999,6 @@ class SlapTool(BaseTool):
parent_uid=self._getComputeNodeUidByReference(
compute_node_reference))
def _getSoftwareInstallationForComputeNode(self, url, compute_node_document):
software_installation_list = self.getPortalObject().portal_catalog.unrestrictedSearchResults(
portal_type='Software Installation',
default_aggregate_uid=compute_node_document.getUid(),
validation_state='validated',
limit=2,
url_string={'query': url, 'key': 'ExactMatch'},
)
l = len(software_installation_list)
if l == 1:
return _assertACI(software_installation_list[0].getObject())
elif l == 0:
raise NotFound('No software release %r found on compute_node %r' % (url,
compute_node_document.getReference()))
else:
raise ValueError('Wrong list of software releases on %r: %s' % (
compute_node_document.getReference(), ', '.join([q.getRelativeUrl() for q \
in software_installation_list])
))
def _getSoftwareInstallationReference(self, url, compute_node_document):
return self._getSoftwareInstallationForComputeNode(url,
compute_node_document).getReference()
def _getSoftwareInstanceForComputePartition(self, compute_node_id,
compute_partition_id, slave_reference=None):
compute_partition_document = self._getComputePartitionDocument(
......@@ -1499,122 +1027,13 @@ class SlapTool(BaseTool):
else:
return software_instance
@UnrestrictedMethod
def _getSoftwareInstanceAsParameterDict(self, software_instance, slave_instance_sql_list=None):
portal = software_instance.getPortalObject()
compute_partition = software_instance.getAggregateValue(portal_type="Compute Partition")
timestamp = int(compute_partition.getModificationDate())
newtimestamp = int(software_instance.getBangTimestamp(int(software_instance.getModificationDate())))
if (newtimestamp > timestamp):
timestamp = newtimestamp
instance_tree = software_instance.getSpecialiseValue()
ip_list = []
full_ip_list = []
for internet_protocol_address in compute_partition.contentValues(portal_type='Internet Protocol Address'):
# XXX - There is new values, and we must keep compatibility
address_tuple = (
internet_protocol_address.getNetworkInterface('').decode("UTF-8"),
internet_protocol_address.getIpAddress().decode("UTF-8"))
if internet_protocol_address.getGatewayIpAddress('') and \
internet_protocol_address.getNetmask(''):
address_tuple = address_tuple + (
internet_protocol_address.getGatewayIpAddress().decode("UTF-8"),
internet_protocol_address.getNetmask().decode("UTF-8"),
internet_protocol_address.getNetworkAddress('').decode("UTF-8"))
full_ip_list.append(address_tuple)
else:
ip_list.append(address_tuple)
slave_instance_list = []
if (software_instance.getPortalType() == "Software Instance"):
append = slave_instance_list.append
if slave_instance_sql_list is None:
slave_instance_sql_list = portal.portal_catalog.unrestrictedSearchResults(
default_aggregate_uid=compute_partition.getUid(),
portal_type='Slave Instance',
validation_state="validated",
**{"slapos_item.slap_state": "start_requested"}
)
for slave_instance in slave_instance_sql_list:
slave_instance = _assertACI(slave_instance.getObject())
# XXX Use catalog to filter more efficiently
if slave_instance.getSlapState() == "start_requested":
newtimestamp = int(slave_instance.getBangTimestamp(int(slave_instance.getModificationDate())))
append({
'slave_title': slave_instance.getTitle().decode("UTF-8"),
'slap_software_type': \
slave_instance.getSourceReference().decode("UTF-8"),
'slave_reference': slave_instance.getReference().decode("UTF-8"),
'timestamp': newtimestamp,
'xml': slave_instance.getTextContent(),
'connection_xml': slave_instance.getConnectionXml(),
})
if (newtimestamp > timestamp):
timestamp = newtimestamp
return {
'instance_guid': software_instance.getReference().decode("UTF-8"),
'instance_title': software_instance.getTitle().decode("UTF-8"),
'root_instance_title': instance_tree.getTitle().decode("UTF-8"),
'root_instance_short_title': instance_tree.getShortTitle().decode("UTF-8"),
'xml': software_instance.getTextContent(),
'connection_xml': software_instance.getConnectionXml(),
'filter_xml': software_instance.getSlaXml(),
'slap_computer_id': \
compute_partition.getParentValue().getReference().decode("UTF-8"),
'slap_computer_partition_id': \
compute_partition.getReference().decode("UTF-8"),
'slap_software_type': \
software_instance.getSourceReference().decode("UTF-8"),
'slap_software_release_url': \
software_instance.getUrlString().decode("UTF-8"),
'slave_instance_list': slave_instance_list,
'ip_list': ip_list,
'full_ip_list': full_ip_list,
'timestamp': "%i" % timestamp,
}
@UnrestrictedMethod
def _getSoftwareReleaseValueListForComputeNode(self, compute_node_reference):
"""Returns list of Software Releases documentsfor compute_node"""
compute_node_document = self._getComputeNodeDocument(compute_node_reference)
portal = self.getPortalObject()
software_release_list = []
for software_installation in portal.portal_catalog.unrestrictedSearchResults(
portal_type='Software Installation',
default_aggregate_uid=compute_node_document.getUid(),
validation_state='validated',
):
software_installation = _assertACI(software_installation.getObject())
software_release_response = SoftwareRelease(
software_release=software_installation.getUrlString().decode('UTF-8'),
computer_guid=compute_node_reference.decode('UTF-8'))
if software_installation.getSlapState() == 'destroy_requested':
software_release_response._requested_state = 'destroyed'
else:
software_release_response._requested_state = 'available'
known_state = software_installation.getTextAccessStatus()
if known_state.startswith("#access"):
software_release_response._known_state = 'available'
elif known_state.startswith("#building"):
software_release_response._known_state = 'building'
else:
software_release_response._known_state = 'error'
software_release_list.append(software_release_response)
return software_release_list
@convertToREST
def _softwareReleaseError(self, url, compute_node_id, error_log):
"""
Log the compute_node status
"""
compute_node_document = self._getComputeNodeDocument(compute_node_id)
software_installation = self._getSoftwareInstallationForComputeNode(url,
compute_node_document)
software_installation.setAccessStatus('#error while installing %s' % url)
compute_node = self._getComputeNodeDocument(compute_node_id)
software_installation = compute_node._getSoftwareInstallationFromUrl(url)
software_installation.setErrorStatus('while installing %s' % url)
InitializeClass(SlapTool)
portal_caches/last_stored_data_cache_factory
portal_caches/last_stored_data_cache_factory/volatile_cache_plugin
portal_caches/compute_node_information_cache_factory
portal_caches/compute_node_information_cache_factory/persistent_cache_plugin
\ No newline at end of file
portal_caches/compute_node_information_cache_factory
portal_caches/compute_node_information_cache_factory/persistent_cache_plugin
portal_caches/slap_cache_factory
portal_caches/slap_cache_factory/persistent_cache_plugin
web_site_module/slapos_hateoas
......
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