Commit d9dcc611 authored by Romain Courteaud's avatar Romain Courteaud

XXX REVERT: stop spreading api code in slapos_cloud

Include code to move
parent d438e92e
##############################################################################
#
# Copyright (c) 2008 Nexedi SA and Contributors. All Rights Reserved.
# Yusei TAHARA <yusei@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions, PropertySheet
from erp5.component.document.Item import Item
from erp5.component.document.JSONType import JSONType
import json
class SoftwareInstallation(Item, JSONType):
"""
This class represents a computer like personal computer, printer, router.
"""
portal_type = 'Software Installation'
add_permission = Permissions.AddPortalContent
# Declarative security
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
# Declarative properties
property_sheets = ( PropertySheet.TextDocument
, PropertySheet.JSONTypeConstraint
)
def useRevision(self):
return getattr(self, "use_jio_api_revision", False)
security.declareProtected(Permissions.AccessContentsInformation,
'asJSONText')
def asJSONText(self):
requested_state = self.getSlapState()
if requested_state == "stop_requested":
state = 'stopped'
elif requested_state in ("start_requested", "started"):
state = 'available'
elif requested_state == "destroy_requested":
state = 'destroyed'
else:
raise ValueError("Unknown slap state : %s" % requested_state)
# software instance has to define an xml parameter
status_dict = self.getAccessStatus()
result = {
"$schema": self.getJSONSchemaUrl(),
"software_release_uri": self.getUrlString(),
"compute_node_id": self.getAggregateReference(),
"state": state,
"reported_state": status_dict.get("state"),
"status_message": status_dict.get("text"),
"portal_type": "Software Installation",
}
if self.useRevision():
web_section = self.getWebSectionValue()
web_section = web_section.getRelativeUrl() if web_section else self.REQUEST.get("web_section_relative_url", None)
if web_section:
revision = self.getJIOAPIRevision(web_section)
if revision:
result["api_revision"] = revision
return json.dumps(result, indent=2)
def getSlapTimestamp(self):
return int(self.getModificationDate())
security.declareProtected(Permissions.AccessContentsInformation,
'getJSONSchemaUrl')
def getJSONSchemaUrl(self):
"""
This is an attempt to provide stability to the Schema URL and by extension to asJSONText
"""
portal = self.getPortalObject()
portal_type_path = portal.portal_types.restrictedTraverse(self.getPortalType())
base_url = portal.portal_preferences.getPreferredSlaposWebSiteUrl().strip("/")
return "/".join([base_url, portal_type_path.getRelativeUrl(), "getTextContent"])
\ No newline at end of file
...@@ -26,11 +26,8 @@ ...@@ -26,11 +26,8 @@
# #
############################################################################## ##############################################################################
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
from App.Common import rfc1123_date
from Products.ERP5Type import Permissions from Products.ERP5Type import Permissions
from erp5.component.document.Item import Item from erp5.component.document.Item import Item
from erp5.component.document.JSONType import JSONType
import json
from lxml import etree from lxml import etree
import collections import collections
...@@ -40,10 +37,12 @@ from erp5.component.module.SlapOSCloud import _assertACI ...@@ -40,10 +37,12 @@ from erp5.component.module.SlapOSCloud import _assertACI
from zLOG import LOG, INFO from zLOG import LOG, INFO
try: try:
from slapos.util import xml2dict from slapos.util import xml2dict, loads
except ImportError: except ImportError:
def xml2dict(dictionary): def xml2dict(dictionary):
raise ImportError raise ImportError
def loads(*args):
raise ImportError
class DisconnectedSoftwareTree(Exception): class DisconnectedSoftwareTree(Exception):
pass pass
...@@ -51,7 +50,7 @@ class DisconnectedSoftwareTree(Exception): ...@@ -51,7 +50,7 @@ class DisconnectedSoftwareTree(Exception):
class CyclicSoftwareTree(Exception): class CyclicSoftwareTree(Exception):
pass pass
class SoftwareInstance(Item, JSONType): class SoftwareInstance(Item):
""" """
""" """
...@@ -158,8 +157,6 @@ class SoftwareInstance(Item, JSONType): ...@@ -158,8 +157,6 @@ class SoftwareInstance(Item, JSONType):
state = 'started' state = 'started'
elif requested_state == "destroy_requested": elif requested_state == "destroy_requested":
state = 'destroyed' state = 'destroyed'
elif requested_state == "draft":
state = 'draft'
else: else:
raise ValueError("Unknown slap state : %s" % requested_state) raise ValueError("Unknown slap state : %s" % requested_state)
...@@ -180,82 +177,38 @@ class SoftwareInstance(Item, JSONType): ...@@ -180,82 +177,38 @@ class SoftwareInstance(Item, JSONType):
software_instance_dict['_instance_guid'] = instance_guid software_instance_dict['_instance_guid'] = instance_guid
return software_instance_dict return software_instance_dict
def getSlapTimestamp(self): def _getModificationDateAsTimestamp(self, document):
return self._getSlapTimestamp() return int(float(document.getModificationDate()) * 1e6)
def useRevision(self):
return getattr(self, "use_jio_api_revision", False)
@UnrestrictedMethod
def _getSlapTimestamp(self):
compute_partition = self.getAggregateValue(portal_type="Compute Partition")
if compute_partition is None:
return int(self.getModificationDate())
timestamp = int(compute_partition.getModificationDate())
newtimestamp = int(self.getBangTimestamp(int(self.getModificationDate())))
if (newtimestamp > timestamp):
timestamp = newtimestamp
# Check if any of the Shared instances hosted in the software instance have been reprocessed
# XXX In the current what shared instances are processed, they cannot be reprocessed if the
# host instance is not processed
if (self.getPortalType() == "Software Instance"):
shared_instance_sql_list = []
if self.useRevision():
shared_instance_sql_list = self.getPortalObject().portal_catalog.unrestrictedSearchResults(
default_aggregate_uid=compute_partition.getUid(),
portal_type='Slave Instance',
validation_state="validated",
sort_on=(("jio_api_revision.revision", "DESC"),),
select_list=('jio_api_revision.revision',),
limit=1,
**{"slapos_item.slap_state": "start_requested"}
)
else:
shared_instance_sql_list = self.getPortalObject().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())))
if (newtimestamp > timestamp):
timestamp = newtimestamp
return timestamp
@UnrestrictedMethod @UnrestrictedMethod
def _asParameterDict(self, shared_instance_sql_list=None): def _asParameterDict(self, shared_instance_sql_list=None):
portal = self.getPortalObject() portal = self.getPortalObject()
compute_partition = self.getAggregateValue(portal_type="Compute Partition") compute_partition = self.getAggregateValue(portal_type="Compute Partition")
if compute_partition is None: if compute_partition is None:
raise ValueError("Instance isn't allocated to call _asParameterDict") raise ValueError("Instance isn't allocated to call _asParamterDict")
timestamp = self.getSlapTimestamp()
timestamp = max(
self._getModificationDateAsTimestamp(compute_partition),
int(self.getBangTimestamp(self._getModificationDateAsTimestamp(self))))
instance_tree = self.getSpecialiseValue() instance_tree = self.getSpecialiseValue()
ip_list = [] ip_list = []
full_ip_list = [] full_ip_list = []
if (self.getPortalType() == "Software Instance"): for internet_protocol_address in compute_partition.contentValues(portal_type='Internet Protocol Address'):
for internet_protocol_address in compute_partition.contentValues(portal_type='Internet Protocol Address'): # XXX - There is new values, and we must keep compatibility
# XXX - There is new values, and we must keep compatibility address_tuple = (
address_tuple = ( internet_protocol_address.getNetworkInterface('').decode("UTF-8"),
internet_protocol_address.getNetworkInterface('').decode("UTF-8"), internet_protocol_address.getIpAddress().decode("UTF-8"))
internet_protocol_address.getIpAddress().decode("UTF-8")) if internet_protocol_address.getGatewayIpAddress('') and \
if internet_protocol_address.getGatewayIpAddress('') and \ internet_protocol_address.getNetmask(''):
internet_protocol_address.getNetmask(''): address_tuple = address_tuple + (
address_tuple = address_tuple + ( internet_protocol_address.getGatewayIpAddress().decode("UTF-8"),
internet_protocol_address.getGatewayIpAddress().decode("UTF-8"), internet_protocol_address.getNetmask().decode("UTF-8"),
internet_protocol_address.getNetmask().decode("UTF-8"), internet_protocol_address.getNetworkAddress('').decode("UTF-8"))
internet_protocol_address.getNetworkAddress('').decode("UTF-8")) full_ip_list.append(address_tuple)
full_ip_list.append(address_tuple) else:
else: ip_list.append(address_tuple)
ip_list.append(address_tuple)
shared_instance_list = [] shared_instance_list = []
if (self.getPortalType() == "Software Instance"): if (self.getPortalType() == "Software Instance"):
...@@ -329,21 +282,19 @@ class SoftwareInstance(Item, JSONType): ...@@ -329,21 +282,19 @@ class SoftwareInstance(Item, JSONType):
return ip_address_list return ip_address_list
def updateRequestedInstanceList(self, instance_reference_list): def _updateSucessorList(self, instance_reference_xml):
return self._updateSucessorList(instance_reference_list)
def _updateSucessorList(self, instance_reference_list):
""" """
Update Software Instance successor list to match the given list. If one Update Software Instance successor list to match the given list. If one
instance was not requested by this compute partition, it should be removed instance was not requested by this compute partition, it should be removed
in the successor_list of this instance. in the successor_list of this instance.
Once the link is removed, this instance will be trashed by Garbage Collect! Once the link is removed, this instance will be trashed by Garbage Collect!
instance_reference_list contain list of title of sub-instances requested by instance_reference_xml contain list of title of sub-instances requested by
this instance. this instance.
""" """
cache_reference = '%s-PREDLIST' % self.getReference() cache_reference = '%s-PREDLIST' % self.getReference()
if not self.isLastData(cache_reference, str(instance_reference_list)): if not self.isLastData(cache_reference, instance_reference_xml):
instance_reference_list = loads(instance_reference_xml)
current_successor_list = self.getSuccessorValueList( current_successor_list = self.getSuccessorValueList(
portal_type=['Software Instance', 'Slave Instance']) portal_type=['Software Instance', 'Slave Instance'])
current_successor_title_list = [i.getTitle() for i in current_successor_list] current_successor_title_list = [i.getTitle() for i in current_successor_list]
...@@ -357,86 +308,4 @@ class SoftwareInstance(Item, JSONType): ...@@ -357,86 +308,4 @@ class SoftwareInstance(Item, JSONType):
self.getReference(), successor_list), error=False) self.getReference(), successor_list), error=False)
self.edit(successor_list=successor_list, self.edit(successor_list=successor_list,
comment='successor_list edited to unlink non commited instances') comment='successor_list edited to unlink non commited instances')
self.setLastData(str(instance_reference_list), key=cache_reference) self.setLastData(instance_reference_xml, key=cache_reference)
\ No newline at end of file
security.declareProtected(Permissions.AccessContentsInformation,
'getJSONSchemaUrl')
def getJSONSchemaUrl(self):
"""
This is an attempt to provide stability to the Schema URL and by extension to asJSONText
"""
portal = self.getPortalObject()
portal_type_path = portal.portal_types.restrictedTraverse("Software Instance")
base_url = portal.portal_preferences.getPreferredSlaposWebSiteUrl().strip("/")
return "/".join([base_url, portal_type_path.getRelativeUrl(), "getTextContent"])
security.declareProtected(Permissions.AccessContentsInformation,
'asJSONText')
def asJSONText(self):
try:
parameter_dict = self._asParameterDict()
except ValueError:
parameter_dict = {}
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'
elif requested_state == "draft":
state = 'draft'
else:
raise ValueError("Unknown slap state : %s" % requested_state)
# software instance has to define an xml parameter
result = {
"$schema": self.getJSONSchemaUrl(),
"title": self.getTitle().decode("UTF-8"),
"reference": self.getReference().decode("UTF-8"),
"software_release_uri": self.getUrlString(),
"software_type": self.getSourceReference().decode("UTF-8"),
"state": state,
"connection_parameters": self.getConnectionXmlAsDict(),
"parameters": self.getInstanceXmlAsDict(),
"shared": False,
"root_instance_title": parameter_dict.get("root_instance_title"),
"ip_list": parameter_dict.get("ip_list"),
"full_ip_list": parameter_dict.get("full_ip_list"),
"sla_parameters": self.getSlaXmlAsDict(),
"compute_node_id": parameter_dict.get("slap_computer_id"),
"compute_partition_id": parameter_dict.get("slap_computer_partition_id"),
"processing_timestamp": self.getSlapTimestamp(),
"access_status_message": self.getTextAccessStatus(),
"portal_type": self.getPortalType(),
}
if self.useRevision():
web_section = self.getWebSectionValue()
web_section = web_section.getRelativeUrl() if web_section else self.REQUEST.get("web_section_relative_url", None)
if web_section:
revision = self.getJIOAPIRevision(web_section)
if revision:
result["api_revision"] = revision
self.REQUEST.response.setHeader('Cache-Control',
'private, max-age=0, must-revalidate')
self.REQUEST.response.setHeader('Vary',
'REMOTE_USER')
self.REQUEST.response.setHeader('Last-Modified',
rfc1123_date(self.getModificationDate()))
return json.dumps(result, indent=2)
security.declareProtected(Permissions.ModifyPortalContent, 'fromJSONText')
def fromJSONText(self, json_content):
return self.setJsonContent(json_content)
def getComputePartitionUid(self):
"""
Get Compute Partition Uid.
Used by software instances as Compute Partition deosn't have access to Compute Node Module.
"""
return self._getComputePartitionUid()
@UnrestrictedMethod
def _getComputePartitionUid(self):
return self.getAggregateUid()
...@@ -30,6 +30,7 @@ from OFS.Traversable import NotFound ...@@ -30,6 +30,7 @@ from OFS.Traversable import NotFound
from Products.ERP5Type.Cache import CachingMethod from Products.ERP5Type.Cache import CachingMethod
from Products.ERP5Type.UnrestrictedMethod import UnrestrictedMethod from Products.ERP5Type.UnrestrictedMethod import UnrestrictedMethod
class SlapOSCatalogToolCacheMixin(object): class SlapOSCatalogToolCacheMixin(object):
""" Quering Caching Extension for Catalog for handle specific queries """ Quering Caching Extension for Catalog for handle specific queries
relying on caching. relying on caching.
...@@ -71,13 +72,9 @@ class SlapOSCatalogToolCacheMixin(object): ...@@ -71,13 +72,9 @@ class SlapOSCatalogToolCacheMixin(object):
@UnrestrictedMethod @UnrestrictedMethod
def _getNonCachedComputeNodeUid(self, reference): def _getNonCachedComputeNodeUid(self, reference):
compute_node_list = self.unrestrictedSearchResults( return self.unrestrictedSearchResults(
portal_type='Compute Node', reference=reference, portal_type=['Compute Node', 'Remote Node'], reference=reference,
validation_state="validated") validation_state="validated")[0].UID
if len(compute_node_list) != 1:
raise NotFound, "No document found with parameters: %s" % reference
return compute_node_list[0].UID
def getComputePartitionObject(self, compute_node_reference, def getComputePartitionObject(self, compute_node_reference,
compute_partition_reference): compute_partition_reference):
...@@ -95,28 +92,3 @@ class SlapOSCatalogToolCacheMixin(object): ...@@ -95,28 +92,3 @@ class SlapOSCatalogToolCacheMixin(object):
(compute_node_reference, compute_partition_reference)) (compute_node_reference, compute_partition_reference))
else: else:
return _assertACI(compute_partition_list[0].getObject()) return _assertACI(compute_partition_list[0].getObject())
def _getNonCachedSoftwareInstance(self, reference, include_shared=False):
# No need to get all results if an error is raised when at least 2 objects
# are found
if not include_shared:
portal_type = "Software Instance"
else:
portal_type = ("Software Instance", "Slave Instance")
software_instance_list = self.unrestrictedSearchResults(limit=2,
portal_type=portal_type,
validation_state="validated",
reference=reference)
if len(software_instance_list) != 1:
raise NotFound, "No document found with parameters: %s" % reference
else:
return _assertACI(software_instance_list[0].getObject()).getRelativeUrl()
def getSoftwareInstanceObject(self, reference, include_shared=False):
"""
Get the validated compute_node with this reference.
"""
result = CachingMethod(self._getNonCachedSoftwareInstance,
id='_getSoftwareInstanceObject',
cache_factory='slap_cache_factory')(reference, include_shared=include_shared)
return self.getPortalObject().restrictedTraverse(result)
\ No newline at end of file
...@@ -6,12 +6,6 @@ ...@@ -6,12 +6,6 @@
</pickle> </pickle>
<pickle> <pickle>
<dictionary> <dictionary>
<item>
<key> <string>_recorded_property_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item> <item>
<key> <string>default_reference</string> </key> <key> <string>default_reference</string> </key>
<value> <string>SlapOSCatalogToolCacheMixin</string> </value> <value> <string>SlapOSCatalogToolCacheMixin</string> </value>
...@@ -61,28 +55,13 @@ ...@@ -61,28 +55,13 @@
<item> <item>
<key> <string>workflow_history</string> </key> <key> <string>workflow_history</string> </key>
<value> <value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent> <persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
</record> </record>
<record id="2" aka="AAAAAAAAAAI="> <record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle> <pickle>
<global name="PersistentMapping" module="Persistence.mapping"/> <global name="PersistentMapping" module="Persistence.mapping"/>
</pickle> </pickle>
...@@ -95,7 +74,7 @@ ...@@ -95,7 +74,7 @@
<item> <item>
<key> <string>component_validation_workflow</string> </key> <key> <string>component_validation_workflow</string> </key>
<value> <value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent> <persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value> </value>
</item> </item>
</dictionary> </dictionary>
...@@ -104,7 +83,7 @@ ...@@ -104,7 +83,7 @@
</dictionary> </dictionary>
</pickle> </pickle>
</record> </record>
<record id="4" aka="AAAAAAAAAAQ="> <record id="3" aka="AAAAAAAAAAM=">
<pickle> <pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/> <global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle> </pickle>
......
...@@ -115,7 +115,9 @@ class SlapOSComputeNodeMixin(object): ...@@ -115,7 +115,9 @@ class SlapOSComputeNodeMixin(object):
dict ( dict (
time=time.time(), time=time.time(),
refresh_etag=refresh_etag, refresh_etag=refresh_etag,
data=computer_dict data=computer_dict,
# Store the XML while SlapTool Still used
data_xml=self.getPortalObject().portal_slap._getSlapComputeNodeXMLFromDict(computer_dict)
), ),
cache_duration=self.getPortalObject().portal_caches\ cache_duration=self.getPortalObject().portal_caches\
.getRamCacheRoot().get('compute_node_information_cache_factory'\ .getRamCacheRoot().get('compute_node_information_cache_factory'\
...@@ -314,7 +316,7 @@ class SlapOSComputeNodeMixin(object): ...@@ -314,7 +316,7 @@ class SlapOSComputeNodeMixin(object):
return partition_dict return partition_dict
def getSoftwareInstallationFromUrl(self, url): def _getSoftwareInstallationFromUrl(self, url):
software_installation_list = self.getPortalObject().portal_catalog.unrestrictedSearchResults( software_installation_list = self.getPortalObject().portal_catalog.unrestrictedSearchResults(
portal_type='Software Installation', portal_type='Software Installation',
default_aggregate_uid=self.getUid(), default_aggregate_uid=self.getUid(),
...@@ -333,71 +335,4 @@ class SlapOSComputeNodeMixin(object): ...@@ -333,71 +335,4 @@ class SlapOSComputeNodeMixin(object):
raise ValueError('Wrong list of software releases on %r: %s' % ( raise ValueError('Wrong list of software releases on %r: %s' % (
self.getReference(), ', '.join([q.getRelativeUrl() for q \ self.getReference(), ', '.join([q.getRelativeUrl() for q \
in software_installation_list]) in software_installation_list])
)) ))
\ No newline at end of file
def getComputePartitionNewsDict(self):
key = '%s_partition_news' % self.getReference()
cache_plugin = self._getCachePlugin()
refresh_etag = self._calculateRefreshEtag()
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):
return self._getCachedComputePartitionNewsDict(key, refresh_etag)
else:
return cached_dict.get('data')
return self._getCachedComputePartitionNewsDict(key, refresh_etag)
def _getCachedComputePartitionNewsDict(self, key, refresh_etag):
unrestrictedSearchResults = self.getPortalObject().portal_catalog.unrestrictedSearchResults
compute_partition_uid_list = [x.uid for x in unrestrictedSearchResults(
parent_uid=self.getUid(),
validation_state="validated",
portal_type="Compute Partition")]
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', 'default_aggregate_title']
)
compute_partition_dict = { }
for software_instance in software_instance_list:
compute_partition_dict[software_instance.default_aggregate_title] = software_instance.getAccessStatus()
try:
self._getCachePlugin().set(key, DEFAULT_CACHE_SCOPE,
dict (
time=time.time(),
refresh_etag=refresh_etag,
data=compute_partition_dict
),
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
return compute_partition_dict
def getJSONSchemaUrl(self):
"""
This is an attempt to provide stability to the Schema URL and by extension to asJSONText
"""
portal = self.getPortalObject()
portal_type_path = portal.portal_types.restrictedTraverse(self.getPortalType())
base_url = portal.portal_preferences.getPreferredSlaposWebSiteUrl().strip("/")
return "/".join([base_url, portal_type_path.getRelativeUrl(), "getTextContent"])
...@@ -45,7 +45,7 @@ except ImportError: ...@@ -45,7 +45,7 @@ except ImportError:
class SlapOSComputePartitionMixin(object): class SlapOSComputePartitionMixin(object):
def getSoftwareInstance(self, slave_reference=None): def _getSoftwareInstance(self, slave_reference=None):
if self.getSlapState() != 'busy': if self.getSlapState() != 'busy':
LOG('SlapOSComputePartitionMixin::_getSoftwareInstance', INFO, LOG('SlapOSComputePartitionMixin::_getSoftwareInstance', INFO,
'Compute partition %s shall be busy, is free' % 'Compute partition %s shall be busy, is free' %
......
# -*- 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.
#
##############################################################################
class SlapOSInstanceTreeMixin(object):
def getSlapTimestamp(self):
return int(self.getModificationDate())
<?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>SlapOSInstanceTreeMixin</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.SlapOSInstanceTreeMixin</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>
...@@ -104,14 +104,6 @@ ...@@ -104,14 +104,6 @@
<tuple/> <tuple/>
</value> </value>
</item> </item>
<item>
<key> <string>type_mixin</string> </key>
<value>
<tuple>
<string>SlapOSInstanceTreeMixin</string>
</tuple>
</value>
</item>
</dictionary> </dictionary>
</pickle> </pickle>
</record> </record>
......
...@@ -102,15 +102,6 @@ ...@@ -102,15 +102,6 @@
<tuple/> <tuple/>
</value> </value>
</item> </item>
<item>
<key> <string>type_mixin</string> </key>
<value>
<tuple>
<string>JIOAPIRevisionMixin</string>
<string>SlapOSCacheMixin</string>
</tuple>
</value>
</item>
</dictionary> </dictionary>
</pickle> </pickle>
</record> </record>
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
<ZopeData> <ZopeData>
<record id="1" aka="AAAAAAAAAAE="> <record id="1" aka="AAAAAAAAAAE=">
<pickle> <pickle>
<global name="JSON Type" module="erp5.portal_type"/> <global name="Base Type" module="erp5.portal_type"/>
</pickle> </pickle>
<pickle> <pickle>
<dictionary> <dictionary>
...@@ -18,6 +18,10 @@ ...@@ -18,6 +18,10 @@
<none/> <none/>
</value> </value>
</item> </item>
<item>
<key> <string>factory</string> </key>
<value> <string>addXMLObject</string> </value>
</item>
<item> <item>
<key> <string>group_list</string> </key> <key> <string>group_list</string> </key>
<value> <value>
...@@ -44,61 +48,22 @@ ...@@ -44,61 +48,22 @@
</item> </item>
<item> <item>
<key> <string>portal_type</string> </key> <key> <string>portal_type</string> </key>
<value> <string>JSON Type</string> </value> <value> <string>Base Type</string> </value>
</item> </item>
<item> <item>
<key> <string>text_content</string> </key> <key> <string>searchable_text_property_id</string> </key>
<value> <string>{\n <value>
"$schema": "http://json-schema.org/draft-07/schema#",\n <tuple>
"$id": "software-instance-base-schema.json",\n <string>title</string>
"title": "Software Installation",\n <string>description</string>
"description": "Software Installation",\n <string>reference</string>
"type": "object",\n <string>short_title</string>
"properties": {\n </tuple>
"software_release_uri": {\n </value>
"title": "Software Release URI",\n
"type": "string"\n
},\n
"compute_node_id": {\n
"title": "Compute Node ID",\n
"type": "string",\n
"description": "The Id of the compute node, example: COMP-1234"\n
},\n
"state": {\n
"title": "Requested State",\n
"type": "string",\n
"enum": ["available", "destroyed"],\n
"description": "State of the requested software",\n
"default": "available"\n
},\n
"reported_state": {\n
"title": "Reported State",\n
"type": "string",\n
"enum": ["available", "destroyed", "building", ""],\n
"description": "State reported by the node installing the Software Installation"\n
},\n
"status_message": {\n
"title": "Status Message",\n
"description": "Last Message received for the Software Installation",\n
"type": "string"\n
},\n
"api_revision": {\n
"title": "API Revision",\n
"type": "string",\n
"description": "The API Revision is set by the master node to mark when the element was last processed. It is incremental. If revision has changed, critical data has been updated"\n
},\n
"portal_type": {\n
"title": "Portal Type",\n
"const": "Software Installation",\n
"type": "string"\n
}\n
}\n
}\n
</string> </value>
</item> </item>
<item> <item>
<key> <string>type_class</string> </key> <key> <string>type_class</string> </key>
<value> <string>SoftwareInstallation</string> </value> <value> <string>Item</string> </value>
</item> </item>
<item> <item>
<key> <string>type_interface</string> </key> <key> <string>type_interface</string> </key>
...@@ -106,15 +71,6 @@ ...@@ -106,15 +71,6 @@
<tuple/> <tuple/>
</value> </value>
</item> </item>
<item>
<key> <string>type_mixin</string> </key>
<value>
<tuple>
<string>JIOAPIRevisionMixin</string>
<string>SlapOSCacheMixin</string>
</tuple>
</value>
</item>
</dictionary> </dictionary>
</pickle> </pickle>
</record> </record>
......
...@@ -2,14 +2,27 @@ ...@@ -2,14 +2,27 @@
<ZopeData> <ZopeData>
<record id="1" aka="AAAAAAAAAAE="> <record id="1" aka="AAAAAAAAAAE=">
<pickle> <pickle>
<global name="JSON Type" module="erp5.portal_type"/> <global name="Base Type" module="erp5.portal_type"/>
</pickle> </pickle>
<pickle> <pickle>
<dictionary> <dictionary>
<item> <item>
<key> <string>_property_domain_dict</string> </key> <key> <string>_property_domain_dict</string> </key>
<value> <value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent> <dictionary>
<item>
<key> <string>short_title</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</value> </value>
</item> </item>
<item> <item>
...@@ -20,6 +33,10 @@ ...@@ -20,6 +33,10 @@
<key> <string>description</string> </key> <key> <string>description</string> </key>
<value> <string>ERP5 default document. Supports synchronisation and XML.</string> </value> <value> <string>ERP5 default document. Supports synchronisation and XML.</string> </value>
</item> </item>
<item>
<key> <string>factory</string> </key>
<value> <string>addSoftwareInstance</string> </value>
</item>
<item> <item>
<key> <string>group_list</string> </key> <key> <string>group_list</string> </key>
<value> <value>
...@@ -28,14 +45,26 @@ ...@@ -28,14 +45,26 @@
</tuple> </tuple>
</value> </value>
</item> </item>
<item>
<key> <string>icon</string> </key>
<value> <string>document_icon.gif</string> </value>
</item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>Software Instance</string> </value> <value> <string>Software Instance</string> </value>
</item> </item>
<item>
<key> <string>immediate_view</string> </key>
<value> <string>XMLObject_view</string> </value>
</item>
<item> <item>
<key> <string>init_script</string> </key> <key> <string>init_script</string> </key>
<value> <string>SoftwareInstance_init</string> </value> <value> <string>SoftwareInstance_init</string> </value>
</item> </item>
<item>
<key> <string>meta_type</string> </key>
<value> <string>ERP5 XML Object</string> </value>
</item>
<item> <item>
<key> <string>permission</string> </key> <key> <string>permission</string> </key>
<value> <value>
...@@ -44,7 +73,11 @@ ...@@ -44,7 +73,11 @@
</item> </item>
<item> <item>
<key> <string>portal_type</string> </key> <key> <string>portal_type</string> </key>
<value> <string>JSON Type</string> </value> <value> <string>Base Type</string> </value>
</item>
<item>
<key> <string>product</string> </key>
<value> <string>ERP5Type</string> </value>
</item> </item>
<item> <item>
<key> <string>searchable_text_property_id</string> </key> <key> <string>searchable_text_property_id</string> </key>
...@@ -57,159 +90,6 @@ ...@@ -57,159 +90,6 @@
</tuple> </tuple>
</value> </value>
</item> </item>
<item>
<key> <string>text_content</string> </key>
<value> <string>{\n
"$schema": "http://json-schema.org/draft-07/schema#",\n
"$id": "software-instance-base-schema.json",\n
"title": "Software Instance",\n
"description": "Software Instance",\n
"type": "object",\n
"properties": {\n
"$schema": {\n
"title": "Schema URL",\n
"type": "string",\n
"description": "URL of the response schema"\n
},\n
"title": {\n
"title": "Title",\n
"type": "string",\n
"description": "Unique Name of the Software Instance",\n
"maxLength": 200\n
},\n
"reference": {\n
"title": "Software Instance Reference",\n
"description": "Unique identifier of the Software Instance",\n
"type": "string",\n
"maxLength": 50\n
},\n
"software_release_uri": {\n
"title": "Software Release URI",\n
"type": "string",\n
"description": "URL of the software release used by the software instance"\n
},\n
"software_type": {\n
"title": "Software Type",\n
"type": "string",\n
"description": "Software type of the instance, it define the type of the instance according to the software release"\n
},\n
"state": {\n
"title": "Requested State",\n
"type": "string",\n
"enum": ["started", "stopped", "destroyed"],\n
"description": "State of the requested instance. It is functionnal when started. In stopped state, all services are stopped. If destroyed, it will remove the instance."\n
},\n
"connection_parameters": {\n
"title": "Connections Parameters",\n
"type": "object",\n
"description": "List of parameters provided by the instance to access it. Updated by the instance.",\n
"additionalProperties": { "type": "string" }\n
},\n
"parameters": {\n
"title": "Instance Parameters",\n
"type": "object",\n
"description": "Parameters provided to the instance when requested to configure it.",\n
"additionalProperties": { "type": "string" }\n
},\n
"shared": {\n
"title": "Shared Instance",\n
"type": "boolean",\n
"description": "This instance is Shared Instance hosted on another instance. It doesn\'t use a computer partition."\n
},\n
"root_instance_title": {\n
"title": "Root Instance Title",\n
"type": "string",\n
"description": "Title of the Instance at the root of the instance tree. This is the title of the instance that has been requested by the user."\n
},\n
"ip_list": {\n
"title": "IP List",\n
"type": "array",\n
"descritpion": "List of IPs usable by the Software Instance on the partition",\n
"items": {\n
"type": "array",\n
"items": {\n
"type": "string"\n
}\n
}\n
},\n
"full_ip_list": {\n
"title": "Full IP List",\n
"type": "array",\n
"descritpion": "XXXX List of IPs usable by the Software Instance on the partition",\n
"items": {\n
"type": "array",\n
"items": {\n
"type": "string"\n
}\n
}\n
},\n
"sla_parameters": {\n
"title": "Target Node Selection Parameters",\n
"type": "object",\n
"description": "Also known as SLA parameters. Used to pick where an how the instance is to be deployed",\n
"properties": {\n
"computer_guid": {\n
"title": "Requested Compute Node",\n
"descritpion": "Requested Compute Node Reference, like COMP-1234",\n
"type": "string"\n
},\n
"project_guid": {\n
"title": "Requested Project",\n
"descritpion": "Requested Project Reference",\n
"type": "string"\n
},\n
"instance_guid": {\n
"title": "Requested Host Instance",\n
"descritpion": "Only applicable to shared instance. Requested Host Instance Reference, like SOFTINST-1234",\n
"type": "string"\n
},\n
"network_guid": {\n
"title": "Requested Network",\n
"descritpion": "Requested Network Reference",\n
"type": "string"\n
},\n
"retention_delay": {\n
"title": "Retention Delay",\n
"Description": "Number of days during the data is preserved after the instance is destroyed",\n
"type": "number"\n
}\n
},\n
"additionalProperties": { "type": "string" }\n
},\n
"compute_node_id": {\n
"title": "Compute Node Id",\n
"type": "string",\n
"description": "Id Of the Requesting Compute Node, used by Slap Client when an instance is requesting an instance"\n
},\n
"compute_partition_id": {\n
"title": "Compute Partition Id",\n
"type": "string",\n
"description": "Id Of the Requesting Compute Partition, used by Slap Client when an instance is requesting an instance"\n
},\n
"processing_timestamp": {\n
"title": "Processing Timestamp",\n
"type": "number",\n
"description": "Timestamp set by the master node to mark when it was last processed in the master node. If it has been processed on master, it needs reprocessiing on the compute node."\n
},\n
"api_revision": {\n
"title": "API Revision",\n
"type": "string",\n
"description": "The API Revision is set by the master node to mark when the element was last processed. It is incremental. If revision has changed, critical data has been updated"\n
},\n
"access_status_message": {\n
"title": "Access Status",\n
"type": "string",\n
"description": "Get latest software instance Access message state"\n
},\n
"portal_type": {\n
"title": "Portal Type",\n
"const": "Software Instance",\n
"type": "string"\n
}\n
}\n
}\n
</string> </value>
</item>
<item> <item>
<key> <string>type_class</string> </key> <key> <string>type_class</string> </key>
<value> <string>SoftwareInstance</string> </value> <value> <string>SoftwareInstance</string> </value>
...@@ -220,72 +100,10 @@ ...@@ -220,72 +100,10 @@
<tuple/> <tuple/>
</value> </value>
</item> </item>
<item>
<key> <string>type_mixin</string> </key>
<value>
<tuple>
<string>JIOAPIRevisionMixin</string>
<string>SlapOSCacheMixin</string>
</tuple>
</value>
</item>
</dictionary> </dictionary>
</pickle> </pickle>
</record> </record>
<record id="2" aka="AAAAAAAAAAI="> <record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>description</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>short_title</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAU=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="TranslationInformation" module="Products.ERP5Type.TranslationProviderBase"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>domain_name</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>property_name</string> </key>
<value> <string>description</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle> <pickle>
<global name="TranslationInformation" module="Products.ERP5Type.TranslationProviderBase"/> <global name="TranslationInformation" module="Products.ERP5Type.TranslationProviderBase"/>
</pickle> </pickle>
...@@ -302,7 +120,7 @@ ...@@ -302,7 +120,7 @@
</dictionary> </dictionary>
</pickle> </pickle>
</record> </record>
<record id="5" aka="AAAAAAAAAAU="> <record id="3" aka="AAAAAAAAAAM=">
<pickle> <pickle>
<global name="TranslationInformation" module="Products.ERP5Type.TranslationProviderBase"/> <global name="TranslationInformation" module="Products.ERP5Type.TranslationProviderBase"/>
</pickle> </pickle>
......
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
<item>SlapOSCatalogToolCacheMixin</item> <item>SlapOSCatalogToolCacheMixin</item>
</portal_type> </portal_type>
<portal_type id="Compute Node"> <portal_type id="Compute Node">
<item>JIOAPIRevisionMixin</item>
<item>SlapOSCacheMixin</item> <item>SlapOSCacheMixin</item>
<item>SlapOSComputeNodeMixin</item> <item>SlapOSComputeNodeMixin</item>
</portal_type> </portal_type>
...@@ -11,8 +10,8 @@ ...@@ -11,8 +10,8 @@
<item>SlapOSCacheMixin</item> <item>SlapOSCacheMixin</item>
<item>SlapOSComputePartitionMixin</item> <item>SlapOSComputePartitionMixin</item>
</portal_type> </portal_type>
<portal_type id="Instance Tree"> <portal_type id="Instance Node">
<item>SlapOSInstanceTreeMixin</item> <item>SlapOSCacheMixin</item>
</portal_type> </portal_type>
<portal_type id="Person"> <portal_type id="Person">
<item>SlapOSCacheMixin</item> <item>SlapOSCacheMixin</item>
...@@ -28,12 +27,5 @@ ...@@ -28,12 +27,5 @@
</portal_type> </portal_type>
<portal_type id="Software Instance"> <portal_type id="Software Instance">
<item>SlapOSCacheMixin</item> <item>SlapOSCacheMixin</item>
<item>JIOAPIRevisionMixin</item>
</portal_type>
<portal_type id="Software Installation">
<item>JIOAPIRevisionMixin</item>
</portal_type>
<portal_type id="Software Instance">
<item>JIOAPIRevisionMixin</item>
</portal_type> </portal_type>
</type_mixin> </type_mixin>
\ No newline at end of file
...@@ -100,7 +100,6 @@ ...@@ -100,7 +100,6 @@
<value> <value>
<list> <list>
<string>my_preferred_hateoas_url</string> <string>my_preferred_hateoas_url</string>
<string>my_preferred_jio_api_url</string>
<string>my_preferred_slapos_web_site_url</string> <string>my_preferred_slapos_web_site_url</string>
<string>my_preferred_shacache_website_expected_state</string> <string>my_preferred_shacache_website_expected_state</string>
<string>my_preferred_wechat_integration_site</string> <string>my_preferred_wechat_integration_site</string>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>title</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>my_preferred_jio_api_url</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>items</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_reference</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>items</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string>Click to edit the target</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Preferred JIO API URL</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="TALESMethod" module="Products.Formulator.TALESField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_text</string> </key>
<value> <string>python: [(\'\', \'\')] + [(x.getTitle(), x.getRelativeUrl()) for x in here.portal_catalog(portal_type=\'Service\', sort_on=((\'title\', \'ASC\'),),checked_permission=\'View\')]</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
# -*- coding:utf-8 -*-
##############################################################################
#
# Copyright (c) 2019 Nexedi SA and Contributors. All Rights Reserved.
#
# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
##############################################################################
import six
import six.moves.urllib.parse
from erp5.component.test.testSlapOSCloudSecurityGroup import TestSlapOSSecurityMixin
from erp5.component.test.SlapOSTestCaseMixin import changeSkin
import re
import xml_marshaller
from AccessControl.SecurityManagement import getSecurityManager, \
setSecurityManager
class DefaultScenarioMixin(TestSlapOSSecurityMixin):
def afterSetUp(self):
TestSlapOSSecurityMixin.afterSetUp(self)
preference = self.portal.portal_preferences.slapos_default_system_preference
preference.edit(
preferred_credential_alarm_automatic_call=0,
preferred_credential_recovery_automatic_approval=0,
preferred_credential_request_automatic_approval=1
)
"""
# Enable alarms for regularisation request
self.portal.portal_alarms.slapos_crm_create_regularisation_request.setEnabled(True)
self.portal.portal_alarms.slapos_crm_invalidate_suspended_regularisation_request.setEnabled(True)
self.portal.portal_alarms.slapos_crm_trigger_stop_reminder_escalation.setEnabled(True)
self.portal.portal_alarms.slapos_crm_trigger_stop_acknowledgment_escalation.setEnabled(True)
self.portal.portal_alarms.slapos_crm_trigger_delete_reminder_escalation.setEnabled(True)
self.portal.portal_alarms.slapos_crm_trigger_acknowledgment_escalation.setEnabled(True)
"""
# Create akarls steps
self.createAlarmStep()
def createProject(self):
project = self.portal.project_module.newContent(
portal_type='Project',
title='project-%s' % self.generateNewId()
)
project.validate()
return project
def createAdminUser(self, project):
""" Create a Admin user, to manage compute_nodes and instances eventually """
admin_user_login = self.portal.portal_catalog.getResultValue(
portal_type="ERP5 Login",
reference="admin_user",
validation_state="validated"
)
if admin_user_login is None:
admin_user = self.portal.person_module\
.newContent(portal_type="Person")
admin_user.newContent(
portal_type="ERP5 Login",
reference="admin_user").validate()
admin_user.edit(
first_name="Admin User",
reference="Admin_user",
default_email_text="do_not_reply_to_admin@example.org",
)
admin_user.validate()
else:
admin_user = admin_user_login.getParentValue()
admin_user.newContent(
portal_type='Assignment',
destination_project_value=project,
function='production/manager'
).open()
self.admin_user = admin_user
@changeSkin('Hal')
def joinSlapOS(self, web_site, reference):
def findMessage(email, body):
for candidate in reversed(self.portal.MailHost.getMessageList()):
if [q for q in candidate[1] if email in q] and body in candidate[2]:
return candidate[2]
self.portal.portal_skins.changeSkin('RJS')
credential_request_form = self.web_site.slapos_master_panel.hateoas.connection.join_form()
expected_message = 'You will receive a confirmation email to activate your account.'
self.assertTrue(expected_message in credential_request_form,
'%s not in %s' % (expected_message, credential_request_form))
email = '%s@example.com' % reference
redirect_url = self.web_site.slapos_master_panel.hateoas.connection.WebSection_newCredentialRequest(
reference=reference,
default_email_text=email,
first_name="Joe",
last_name=reference,
password="demo_functional_user",
default_telephone_text="12345678",
corporate_name="Nexedi",
default_address_city="Campos",
default_address_street_address="Av Pelinca",
default_address_zip_code="28480",
default_address_region='europe/west/france',
)
parsed_url = six.moves.urllib.parse.urlparse(redirect_url)
self.assertEqual(parsed_url.path.split('/')[-1], 'login_form')
self.assertEqual(
sorted(six.iteritems(dict(six.moves.urllib.parse.parse_qsl(parsed_url.query)))), [
('portal_status_message', 'Thank you for your registration. You will receive an email to activate your account.'),
])
self.tic()
to_click_message = findMessage(email, 'You have requested one user')
self.assertNotEqual(None, to_click_message)
to_click_url = re.search('href="(.+?)"', to_click_message).group(1)
self.assertIn('%s/hateoas/connection/ERP5Site_activeLogin' % self.web_site.getId(), to_click_url)
join_key = to_click_url.split('=')[-1]
self.assertNotEqual(join_key, None)
self.portal.portal_skins.changeSkin('RJS')
web_site.slapos_master_panel.hateoas.connection.ERP5Site_activeLogin(key=join_key)
self.assertEqual(self.portal.REQUEST.RESPONSE.getStatus(), 303)
self.assertIn(self.web_site.getId() + "/%23%21login%3Fp.page%3Dslapos%7B%26n.me%7D",
self.portal.REQUEST.RESPONSE.getHeader("Location"))
self.tic()
welcome_message = findMessage(email, "the creation of you new")
self.assertNotEqual(None, welcome_message)
def _getCurrentInstanceTreeList(self):
person = self.portal.portal_membership.getAuthenticatedMember().getUserValue()
if person is not None:
return self.portal.portal_catalog(
portal_type="Instance Tree",
default_destination_section_uid=person.getUid(),
validation_state='validated')
return []
def setAccessToMemcached(self, agent):
agent.setAccessStatus("#access ")
def requestComputeNode(self, title, project_reference):
requestXml = self.portal.portal_slap.requestComputer(title, project_reference)
self.tic()
self.assertIn('marshal', requestXml)
compute_node = xml_marshaller.xml_marshaller.loads(requestXml)
compute_node_id = getattr(compute_node, '_computer_id', None)
self.assertNotEqual(None, compute_node_id)
return compute_node_id.encode('UTF-8')
def supplySoftware(self, server, url, state='available'):
self.portal.portal_slap.supplySupply(url, server.getReference(), state)
self.tic()
self.cleanUpRequest()
software_installation = self.portal.portal_catalog.getResultValue(
portal_type='Software Installation',
url_string=url,
default_aggregate_uid=server.getUid())
self.assertNotEqual(None, software_installation)
if state=='available':
self.assertEqual('start_requested', software_installation.getSlapState())
else:
self.assertEqual('destroy_requested', software_installation.getSlapState())
@changeSkin('RJS')
def setServerOpenPublic(self, server):
server.edit(
allocation_scope='open')
self.assertEqual('open', server.getAllocationScope())
self.assertEqual('close', server.getCapacityScope())
server.edit(capacity_scope='open')
self.tic()
def formatComputeNode(self, compute_node, partition_count=10):
compute_node_dict = dict(
software_root='/opt',
reference=compute_node.getReference(),
netmask='255.255.255.0',
address='128.0.0.1',
instance_root='/srv'
)
compute_node_dict['partition_list'] = []
a = compute_node_dict['partition_list'].append
for i in range(1, partition_count+1):
a(dict(
reference='part%s' % i,
tap=dict(name='tap%s' % i),
address_list=[
dict(addr='p%sa1' % i, netmask='p%sn1' % i),
dict(addr='p%sa2' % i, netmask='p%sn2' % i)
]
))
sm = getSecurityManager()
try:
self.login(compute_node.getUserId())
self.portal.portal_slap.loadComputerConfigurationFromXML(
xml_marshaller.xml_marshaller.dumps(compute_node_dict))
self.tic()
self.assertEqual(partition_count,
len(compute_node.contentValues(portal_type='Compute Partition')))
finally:
setSecurityManager(sm)
def simulateSlapgridSR(self, compute_node):
sm = getSecurityManager()
compute_node_user_id = compute_node.getUserId()
try:
self.login(compute_node_user_id)
compute_node_xml = self.portal.portal_slap.getFullComputerInformation(
computer_id=compute_node.getReference())
if not isinstance(compute_node_xml, str):
compute_node_xml = compute_node_xml.getBody()
slap_compute_node = xml_marshaller.xml_marshaller.loads(compute_node_xml)
self.assertEqual('Computer', slap_compute_node.__class__.__name__)
for software_release in slap_compute_node._software_release_list:
if software_release._requested_state == 'destroyed':
self.portal.portal_slap.destroyedSoftwareRelease(
software_release._software_release,
compute_node.getReference())
else:
self.portal.portal_slap.availableSoftwareRelease(
software_release._software_release,
compute_node.getReference())
finally:
setSecurityManager(sm)
self.tic()
def simulateSlapgridUR(self, compute_node):
sm = getSecurityManager()
compute_node_user_id = compute_node.getUserId()
try:
self.login(compute_node_user_id)
compute_node_xml = self.portal.portal_slap.getFullComputerInformation(
computer_id=compute_node.getReference())
if not isinstance(compute_node_xml, str):
compute_node_xml = compute_node_xml.getBody()
slap_compute_node = xml_marshaller.xml_marshaller.loads(compute_node_xml)
self.assertEqual('Computer', slap_compute_node.__class__.__name__)
destroyed_partition_id_list = []
for partition in slap_compute_node._computer_partition_list:
if partition._requested_state == 'destroyed' \
and partition._need_modification == 1:
self.portal.portal_slap.destroyedComputerPartition(compute_node.getReference(),
partition._partition_id.encode("UTF-8")
)
destroyed_partition_id_list.append(partition._partition_id.encode("UTF-8"))
finally:
setSecurityManager(sm)
self.tic()
free_partition_id_list = []
for partition in compute_node.contentValues(portal_type='Compute Partition'):
if partition.getReference() in destroyed_partition_id_list \
and partition.getSlapState() == 'free':
free_partition_id_list.append(partition.getReference())
self.assertSameSet(destroyed_partition_id_list, free_partition_id_list)
def simulateSlapgridCP(self, compute_node):
sm = getSecurityManager()
compute_node_reference = compute_node.getReference()
compute_node_user_id = compute_node.getUserId()
try:
self.login(compute_node_user_id)
compute_node_xml = self.portal.portal_slap.getFullComputerInformation(
computer_id=compute_node.getReference())
if not isinstance(compute_node_xml, str):
compute_node_xml = compute_node_xml.getBody()
slap_compute_node = xml_marshaller.xml_marshaller.loads(compute_node_xml)
self.assertEqual('Computer', slap_compute_node.__class__.__name__)
for partition in slap_compute_node._computer_partition_list:
if partition._requested_state in ('started', 'stopped') \
and partition._need_modification == 1:
instance_reference = partition._instance_guid.encode('UTF-8')
ip_list = partition._parameter_dict['ip_list']
connection_xml = xml_marshaller.xml_marshaller.dumps(dict(
url_1 = 'http://%s/' % ip_list[0][1],
url_2 = 'http://%s/' % ip_list[1][1],
))
self.login()
instance_user_id = self.portal.portal_catalog.getResultValue(
reference=instance_reference, portal_type="Software Instance").getUserId()
oldsm = getSecurityManager()
try:
self.login(instance_user_id)
self.portal.portal_slap.setComputerPartitionConnectionXml(
computer_id=compute_node_reference,
computer_partition_id=partition._partition_id,
connection_xml=connection_xml
)
for slave in partition._parameter_dict['slave_instance_list']:
slave_reference = slave['slave_reference']
connection_xml = xml_marshaller.xml_marshaller.dumps(dict(
url_1 = 'http://%s/%s' % (ip_list[0][1], slave_reference),
url_2 = 'http://%s/%s' % (ip_list[1][1], slave_reference)
))
self.portal.portal_slap.setComputerPartitionConnectionXml(
computer_id=compute_node_reference,
computer_partition_id=partition._partition_id,
connection_xml=connection_xml,
slave_reference=slave_reference
)
finally:
setSecurityManager(oldsm)
finally:
setSecurityManager(sm)
self.tic()
def personRequestInstanceNotReady(self, **kw):
response = self.portal.portal_slap.requestComputerPartition(**kw)
status = getattr(response, 'status', None)
self.assertEqual(408, status)
self.tic()
def personRequestInstance(self, **kw):
response = self.portal.portal_slap.requestComputerPartition(**kw)
self.assertTrue(isinstance(response, str), "response is not a string: %s" % response)
software_instance = xml_marshaller.xml_marshaller.loads(response)
self.assertEqual('SoftwareInstance', software_instance.__class__.__name__)
self.tic()
return software_instance
def checkSlaveInstanceAllocation(self, person_user_id, person_reference,
instance_title, software_release, software_type, server,
project_reference):
self.tic()
self.login(person_user_id)
self.personRequestInstanceNotReady(
software_release=software_release,
software_type=software_type,
partition_reference=instance_title,
shared_xml='<marshal><bool>1</bool></marshal>',
project_reference=project_reference
)
# XXX search only for this user
instance_tree = self.portal.portal_catalog.getResultValue(
portal_type="Instance Tree",
title=instance_title,
follow_up__reference=project_reference
)
self.checkServiceSubscriptionRequest(instance_tree)
self.tic()
self.login(person_user_id)
self.personRequestInstance(
software_release=software_release,
software_type=software_type,
partition_reference=instance_title,
shared_xml='<marshal><bool>1</bool></marshal>',
project_reference=project_reference
)
# now instantiate it on compute_node and set some nice connection dict
self.simulateSlapgridCP(server)
# let's find instances of user and check connection strings
instance_tree_list = [q.getObject() for q in
self._getCurrentInstanceTreeList()
if q.getTitle() == instance_title]
self.assertEqual(1, len(instance_tree_list))
instance_tree = instance_tree_list[0]
software_instance = instance_tree.getSuccessorValue()
self.assertEqual(software_instance.getTitle(),
instance_tree.getTitle())
connection_dict = software_instance.getConnectionXmlAsDict()
self.assertSameSet(('url_1', 'url_2'), connection_dict.keys())
self.login()
partition = software_instance.getAggregateValue()
self.assertSameSet(
['http://%s/%s' % (q.getIpAddress(), software_instance.getReference())
for q in partition.contentValues(
portal_type='Internet Protocol Address')],
connection_dict.values())
def checkInstanceTreeSlaveInstanceAllocation(
self,
person_user_id,
person_reference,
instance_tree_title, instance_title, software_release, software_type,
server,
project_reference
):
self.login(person_user_id)
# let's find instance of user
instance_tree_list = [q.getObject() for q in
self._getCurrentInstanceTreeList()
if q.getTitle() == instance_tree_title]
self.assertEqual(1, len(instance_tree_list))
instance_tree = instance_tree_list[0]
software_instance = instance_tree.getSuccessorValue()
self.login()
instance_user_id = software_instance.getUserId()
compute_partition = software_instance.getAggregateValue()
computer_id = compute_partition.getParentValue().getReference()
computer_partition_id = compute_partition.getTitle()
self.login(instance_user_id)
response = self.portal.portal_slap.requestComputerPartition(
computer_id=computer_id,
computer_partition_id=computer_partition_id,
software_release=software_release,
software_type=software_type,
partition_reference=instance_title,
shared_xml='<marshal><bool>2</bool></marshal>',
project_reference=project_reference
)
status = getattr(response, 'status', None)
self.assertEqual(408, status)
self.tic()
# now instantiate it on compute_node and set some nice connection dict
self.simulateSlapgridCP(server)
# let's find instances of user and check connection strings
slave_instance = software_instance.getSuccessorValue()
connection_dict = slave_instance.getConnectionXmlAsDict()
self.assertSameSet(('url_1', 'url_2'), connection_dict.keys())
self.login()
partition = slave_instance.getAggregateValue()
self.assertSameSet(
['http://%s/%s' % (q.getIpAddress(), slave_instance.getReference())
for q in partition.contentValues(
portal_type='Internet Protocol Address')],
connection_dict.values())
def checkRemoteInstanceAllocation(self, person_user_id, person_reference,
instance_title, software_release, software_type, server,
project_reference, connection_dict_to_check=None,
slave=False):
shared_xml = '<marshal><bool>%i</bool></marshal>' % int(slave)
self.login(person_user_id)
if connection_dict_to_check is None:
self.personRequestInstanceNotReady(
software_release=software_release,
software_type=software_type,
partition_reference=instance_title,
project_reference=project_reference,
shared_xml=shared_xml,
)
# XXX search only for this user
instance_tree = self.portal.portal_catalog.getResultValue(
portal_type="Instance Tree",
title=instance_title,
follow_up__reference=project_reference
)
self.checkServiceSubscriptionRequest(instance_tree)
self.tic()
self.login(person_user_id)
self.personRequestInstance(
software_release=software_release,
software_type=software_type,
partition_reference=instance_title,
project_reference=project_reference,
shared_xml=shared_xml,
)
# now instantiate it on compute_node and set some nice connection dict
# XXX XXX self.simulateSlapgridCP(server)
# let's find instances of user and check connection strings
instance_tree_list = [q.getObject() for q in
self._getCurrentInstanceTreeList()
if q.getTitle() == instance_title]
self.assertEqual(1, len(instance_tree_list))
instance_tree = instance_tree_list[0]
software_instance = instance_tree.getSuccessorValue()
self.assertEqual(software_instance.getTitle(),
instance_tree.getTitle())
connection_dict = software_instance.getConnectionXmlAsDict()
if connection_dict_to_check is None:
connection_dict_to_check = {}
self.assertSameSet(connection_dict_to_check.keys(), connection_dict.keys())
self.assertSameSet(
connection_dict_to_check.values(),
connection_dict.values())
def checkSlaveInstanceUnallocation(self, person_user_id,
person_reference, instance_title,
software_release, software_type, server,
project_reference):
self.login(person_user_id)
self.personRequestInstanceNotReady(
software_release=software_release,
software_type=software_type,
partition_reference=instance_title,
shared_xml='<marshal><bool>1</bool></marshal>',
state='<marshal><string>destroyed</string></marshal>',
project_reference=project_reference
)
# let's find instances of user and check connection strings
instance_tree_list = [q.getObject() for q in
self._getCurrentInstanceTreeList()
if q.getTitle() == instance_title]
self.assertEqual(0, len(instance_tree_list))
def checkRemoteInstanceUnallocation(self, person_user_id,
person_reference, instance_title,
software_release, software_type, server,
project_reference):
self.login(person_user_id)
self.personRequestInstanceNotReady(
software_release=software_release,
software_type=software_type,
partition_reference=instance_title,
state='<marshal><string>destroyed</string></marshal>',
project_reference=project_reference
)
# let's find instances of user and check connection strings
instance_tree_list = [q.getObject() for q in
self._getCurrentInstanceTreeList()
if q.getTitle() == instance_title]
self.assertEqual(0, len(instance_tree_list))
def checkInstanceUnallocation(self, person_user_id,
person_reference, instance_title,
software_release, software_type, server, project_reference):
self.login(person_user_id)
self.personRequestInstanceNotReady(
software_release=software_release,
software_type=software_type,
partition_reference=instance_title,
state='<marshal><string>destroyed</string></marshal>',
project_reference=project_reference
)
# now instantiate it on compute_node and set some nice connection dict
self.simulateSlapgridUR(server)
# let's find instances of user and check connection strings
instance_tree_list = [q.getObject() for q in
self._getCurrentInstanceTreeList()
if q.getTitle() == instance_title]
self.assertEqual(0, len(instance_tree_list))
def checkServiceSubscriptionRequest(self, service, simulation_state='invalidated'):
self.login()
subscription_request = self.portal.portal_catalog.getResultValue(
portal_type="Subscription Request",
aggregate__uid=service.getUid(),
simulation_state=simulation_state
)
self.assertNotEqual(subscription_request, None,
"Not found subscription request for %s" % service.getRelativeUrl())
return subscription_request
def checkInstanceAllocation(self, person_user_id, person_reference,
instance_title, software_release, software_type, server,
project_reference):
self.login(person_user_id)
self.personRequestInstanceNotReady(
software_release=software_release,
software_type=software_type,
partition_reference=instance_title,
project_reference=project_reference
)
self.tic()
# XXX search only for this user
instance_tree = self.portal.portal_catalog.getResultValue(
portal_type="Instance Tree",
title=instance_title,
follow_up__reference=project_reference
)
self.checkServiceSubscriptionRequest(instance_tree)
self.tic()
self.login(person_user_id)
self.personRequestInstance(
software_release=software_release,
software_type=software_type,
partition_reference=instance_title,
project_reference=project_reference
)
# now instantiate it on compute_node and set some nice connection dict
self.simulateSlapgridCP(server)
# let's find instances of user and check connection strings
instance_tree_list = [q.getObject() for q in
self._getCurrentInstanceTreeList()
if q.getTitle() == instance_title]
self.assertEqual(1, len(instance_tree_list))
instance_tree = instance_tree_list[0]
software_instance = instance_tree.getSuccessorValue()
self.assertEqual(software_instance.getTitle(),
instance_tree.getTitle())
connection_dict = software_instance.getConnectionXmlAsDict()
self.assertSameSet(('url_1', 'url_2'), connection_dict.keys())
self.login()
partition = software_instance.getAggregateValue()
self.assertSameSet(
['http://%s/' % q.getIpAddress() for q in
partition.contentValues(portal_type='Internet Protocol Address')],
connection_dict.values())
def checkInstanceAllocationWithDeposit(self, person_user_id, person_reference,
instance_title, software_release, software_type, server,
project_reference, deposit_amount, currency):
self.login(person_user_id)
self.personRequestInstanceNotReady(
software_release=software_release,
software_type=software_type,
partition_reference=instance_title,
project_reference=project_reference
)
self.tic()
instance_tree = self.portal.portal_catalog.getResultValue(
portal_type="Instance Tree",
title=instance_title,
follow_up__reference=project_reference
)
person = instance_tree.getDestinationSectionValue()
self.assertEqual(person.getUserId(), person_user_id)
subscription_request = self.checkServiceSubscriptionRequest(instance_tree, 'submitted')
self.assertEqual(subscription_request.getTotalPrice(), deposit_amount)
self.tic()
outstanding_amount_list = person.Entity_getOutstandingDepositAmountList(
currency.getUid(), ledger_uid=subscription_request.getLedgerUid())
self.assertEqual(sum([i.total_price for i in outstanding_amount_list]), deposit_amount)
self.login(person.getUserId())
# Ensure to pay from the website
outstanding_amount = self.web_site.restrictedTraverse(outstanding_amount_list[0].getRelativeUrl())
outstanding_amount.Base_createExternalPaymentTransactionFromOutstandingAmountAndRedirect()
self.tic()
self.logout()
self.login()
payment_transaction = self.portal.portal_catalog.getResultValue(
portal_type="Payment Transaction",
destination_section_uid=person.getUid(),
simulation_state="started"
)
self.assertEqual(payment_transaction.getSpecialiseValue().getTradeConditionType(), "deposit")
# payzen interface will only stop the payment
payment_transaction.stop()
self.tic()
assert payment_transaction.receivable.getGroupingReference(None) is not None
self.login(person_user_id)
self.checkServiceSubscriptionRequest(instance_tree, 'invalidated')
amount = sum([i.total_price for i in person.Entity_getOutstandingDepositAmountList(
currency.getUid(), ledger_uid=subscription_request.getLedgerUid())])
self.assertEqual(0, amount)
self.login(person_user_id)
self.personRequestInstance(
software_release=software_release,
software_type=software_type,
partition_reference=instance_title,
project_reference=project_reference
)
# now instantiate it on compute_node and set some nice connection dict
self.simulateSlapgridCP(server)
# let's find instances of user and check connection strings
instance_tree_list = [q.getObject() for q in
self._getCurrentInstanceTreeList()
if q.getTitle() == instance_title]
self.assertEqual(1, len(instance_tree_list))
instance_tree = instance_tree_list[0]
software_instance = instance_tree.getSuccessorValue()
self.assertEqual(software_instance.getTitle(),
instance_tree.getTitle())
connection_dict = software_instance.getConnectionXmlAsDict()
self.assertSameSet(('url_1', 'url_2'), connection_dict.keys())
self.login()
partition = software_instance.getAggregateValue()
self.assertSameSet(
['http://%s/' % q.getIpAddress() for q in
partition.contentValues(portal_type='Internet Protocol Address')],
connection_dict.values())
def findMessage(self, email, body):
for candidate in reversed(self.portal.MailHost.getMessageList()):
if [q for q in candidate[1] if email in q] and body in candidate[2]:
return candidate[2]
def assertInvoiceNotification(self, person, is_email_expected=True):
if person.getLanguage() == "zh":
expected_message = self.expected_invoice_zh_notification_message
else:
expected_message = self.expected_invoice_en_notification_message
to_click_message = self.findMessage(person.getDefaultEmailText(),
expected_message)
if is_email_expected:
self.assertNotEqual(None, to_click_message)
else:
self.assertEqual(None, to_click_message)
def requestInstance(self, person_user_id, instance_title,
software_release, software_type, project_reference):
self.login(person_user_id)
self.personRequestInstanceNotReady(
software_release=software_release,
software_type=software_type,
partition_reference=instance_title,
project_reference=project_reference
)
...@@ -2,19 +2,13 @@ ...@@ -2,19 +2,13 @@
<ZopeData> <ZopeData>
<record id="1" aka="AAAAAAAAAAE="> <record id="1" aka="AAAAAAAAAAE=">
<pickle> <pickle>
<global name="Document Component" module="erp5.portal_type"/> <global name="Test Component" module="erp5.portal_type"/>
</pickle> </pickle>
<pickle> <pickle>
<dictionary> <dictionary>
<item> <item>
<key> <string>default_reference</string> </key> <key> <string>default_reference</string> </key>
<value> <string>SoftwareInstallation</string> </value> <value> <string>SlapOSTestCaseDefaultScenarioMixin</string> </value>
</item>
<item>
<key> <string>default_source_reference</string> </key>
<value>
<none/>
</value>
</item> </item>
<item> <item>
<key> <string>description</string> </key> <key> <string>description</string> </key>
...@@ -24,11 +18,11 @@ ...@@ -24,11 +18,11 @@
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>document.erp5.SoftwareInstallation</string> </value> <value> <string>test.erp5.SlapOSTestCaseDefaultScenarioMixin</string> </value>
</item> </item>
<item> <item>
<key> <string>portal_type</string> </key> <key> <string>portal_type</string> </key>
<value> <string>Document Component</string> </value> <value> <string>Test Component</string> </value>
</item> </item>
<item> <item>
<key> <string>sid</string> </key> <key> <string>sid</string> </key>
......
...@@ -40,7 +40,7 @@ compute_node_list = portal.portal_catalog.portal_catalog( ...@@ -40,7 +40,7 @@ compute_node_list = portal.portal_catalog.portal_catalog(
if len(compute_node_list) == 2: if len(compute_node_list) == 2:
raise NotImplementedError raise NotImplementedError
elif len(compute_node_list) == 1: elif len(compute_node_list) == 1:
compute_node = compute_node_list[0].getObject() compute_node = compute_node_list[0]
assert compute_node.getFollowUp() == project_list[0].getRelativeUrl() assert compute_node.getFollowUp() == project_list[0].getRelativeUrl()
else: else:
...@@ -57,6 +57,9 @@ else: ...@@ -57,6 +57,9 @@ else:
) )
compute_node.approveComputeNodeRegistration() compute_node.approveComputeNodeRegistration()
compute_node = context.restrictedTraverse(compute_node.getRelativeUrl())
context.REQUEST.set("compute_node", compute_node.getRelativeUrl()) context.REQUEST.set("compute_node", compute_node.getRelativeUrl())
context.REQUEST.set("compute_node_url", compute_node.absolute_url()) context.REQUEST.set("compute_node_url", compute_node.absolute_url())
context.REQUEST.set("compute_node_reference", compute_node.getReference()) context.REQUEST.set("compute_node_reference", compute_node.getReference())
erp5_json_editor erp5_json_editor
erp5_access_token erp5_access_token
erp5_api_style
erp5_item erp5_item
erp5_computer_immobilisation erp5_computer_immobilisation
erp5_software_pdm erp5_software_pdm
slapos_mysql_innodb_catalog slapos_mysql_innodb_catalog
erp5_certificate_authority erp5_certificate_authority
erp5_open_trade erp5_open_trade
erp5_interaction_drop erp5_interaction_drop
\ No newline at end of file
document.erp5.SoftwareInstance document.erp5.SoftwareInstance
document.erp5.SoftwareInstallation \ No newline at end of file
\ No newline at end of file
mixin.erp5.SlapOSCacheMixin mixin.erp5.SlapOSCacheMixin
mixin.erp5.SlapOSComputeNodeMixin mixin.erp5.SlapOSComputeNodeMixin
mixin.erp5.SlapOSComputePartitionMixin mixin.erp5.SlapOSComputePartitionMixin
mixin.erp5.SlapOSCatalogToolCacheMixin mixin.erp5.SlapOSCatalogToolCacheMixin
mixin.erp5.SlapOSInstanceTreeMixin \ No newline at end of file
\ No newline at end of file
Catalog Tool | SlapOSCatalogToolCacheMixin Catalog Tool | SlapOSCatalogToolCacheMixin
Compute Node | JIOAPIRevisionMixin
Compute Node | SlapOSCacheMixin Compute Node | SlapOSCacheMixin
Compute Node | SlapOSComputeNodeMixin Compute Node | SlapOSComputeNodeMixin
Compute Partition | SlapOSCacheMixin Compute Partition | SlapOSCacheMixin
...@@ -8,9 +7,5 @@ Instance Node | SlapOSCacheMixin ...@@ -8,9 +7,5 @@ Instance Node | SlapOSCacheMixin
Person | SlapOSCacheMixin Person | SlapOSCacheMixin
Remote Node | SlapOSCacheMixin Remote Node | SlapOSCacheMixin
Slave Instance | SlapOSCacheMixin Slave Instance | SlapOSCacheMixin
Software Instance | SlapOSCacheMixin
Software Installation | SlapOSCacheMixin Software Installation | SlapOSCacheMixin
Instance Tree | SlapOSInstanceTreeMixin Software Instance | SlapOSCacheMixin
Slave Instance | JIOAPIRevisionMixin \ No newline at end of file
Software Instance | JIOAPIRevisionMixin
Software Installation | JIOAPIRevisionMixin
...@@ -14,5 +14,4 @@ test.erp5.testSlapOSCloudPersonSlapInterfaceWorkflow ...@@ -14,5 +14,4 @@ test.erp5.testSlapOSCloudPersonSlapInterfaceWorkflow
test.erp5.testSlapOSCloudProjectSlapInterfaceWorkflow test.erp5.testSlapOSCloudProjectSlapInterfaceWorkflow
test.erp5.testSlapOSCloudSecurityGroup test.erp5.testSlapOSCloudSecurityGroup
test.erp5.testSlapOSCloudShadow test.erp5.testSlapOSCloudShadow
test.erp5.SlapOSTestCaseMixin test.erp5.testSlapOSCloudUpgrader
test.erp5.testSlapOSCloud \ No newline at end of file
\ No newline at end of file
erp5_full_text_mroonga_catalog
slapos_configurator
\ No newline at end of file
<type_mixin>
<portal_type id="Compute Node">
<item>JIOAPIRevisionMixin</item>
</portal_type>
<portal_type id="Slave Instance">
<item>JIOAPIRevisionMixin</item>
</portal_type>
<portal_type id="Software Installation">
<item>JIOAPIRevisionMixin</item>
</portal_type>
<portal_type id="Software Instance">
<item>JIOAPIRevisionMixin</item>
</portal_type>
</type_mixin>
\ No newline at end of file
Compute Node | JIOAPIRevisionMixin
Slave Instance | JIOAPIRevisionMixin
Software Installation | JIOAPIRevisionMixin
Software Instance | JIOAPIRevisionMixin
\ No newline at end of file
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