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 @@
#
##############################################################################
from AccessControl import ClassSecurityInfo
from App.Common import rfc1123_date
from Products.ERP5Type import Permissions
from erp5.component.document.Item import Item
from erp5.component.document.JSONType import JSONType
import json
from lxml import etree
import collections
......@@ -40,10 +37,12 @@ from erp5.component.module.SlapOSCloud import _assertACI
from zLOG import LOG, INFO
try:
from slapos.util import xml2dict
from slapos.util import xml2dict, loads
except ImportError:
def xml2dict(dictionary):
raise ImportError
def loads(*args):
raise ImportError
class DisconnectedSoftwareTree(Exception):
pass
......@@ -51,7 +50,7 @@ class DisconnectedSoftwareTree(Exception):
class CyclicSoftwareTree(Exception):
pass
class SoftwareInstance(Item, JSONType):
class SoftwareInstance(Item):
"""
"""
......@@ -158,8 +157,6 @@ class SoftwareInstance(Item, JSONType):
state = 'started'
elif requested_state == "destroy_requested":
state = 'destroyed'
elif requested_state == "draft":
state = 'draft'
else:
raise ValueError("Unknown slap state : %s" % requested_state)
......@@ -180,68 +177,24 @@ class SoftwareInstance(Item, JSONType):
software_instance_dict['_instance_guid'] = instance_guid
return software_instance_dict
def getSlapTimestamp(self):
return self._getSlapTimestamp()
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
def _getModificationDateAsTimestamp(self, document):
return int(float(document.getModificationDate()) * 1e6)
@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 _asParameterDict")
timestamp = self.getSlapTimestamp()
raise ValueError("Instance isn't allocated to call _asParamterDict")
timestamp = max(
self._getModificationDateAsTimestamp(compute_partition),
int(self.getBangTimestamp(self._getModificationDateAsTimestamp(self))))
instance_tree = self.getSpecialiseValue()
ip_list = []
full_ip_list = []
if (self.getPortalType() == "Software Instance"):
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 = (
......@@ -329,21 +282,19 @@ class SoftwareInstance(Item, JSONType):
return ip_address_list
def updateRequestedInstanceList(self, instance_reference_list):
return self._updateSucessorList(instance_reference_list)
def _updateSucessorList(self, instance_reference_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_list contain list of title of sub-instances requested by
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, 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(
portal_type=['Software Instance', 'Slave Instance'])
current_successor_title_list = [i.getTitle() for i in current_successor_list]
......@@ -357,86 +308,4 @@ class SoftwareInstance(Item, JSONType):
self.getReference(), successor_list), error=False)
self.edit(successor_list=successor_list,
comment='successor_list edited to unlink non commited instances')
self.setLastData(str(instance_reference_list), key=cache_reference)
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()
self.setLastData(instance_reference_xml, key=cache_reference)
\ No newline at end of file
......@@ -30,6 +30,7 @@ from OFS.Traversable import NotFound
from Products.ERP5Type.Cache import CachingMethod
from Products.ERP5Type.UnrestrictedMethod import UnrestrictedMethod
class SlapOSCatalogToolCacheMixin(object):
""" Quering Caching Extension for Catalog for handle specific queries
relying on caching.
......@@ -71,13 +72,9 @@ class SlapOSCatalogToolCacheMixin(object):
@UnrestrictedMethod
def _getNonCachedComputeNodeUid(self, reference):
compute_node_list = self.unrestrictedSearchResults(
portal_type='Compute Node', reference=reference,
validation_state="validated")
if len(compute_node_list) != 1:
raise NotFound, "No document found with parameters: %s" % reference
return compute_node_list[0].UID
return self.unrestrictedSearchResults(
portal_type=['Compute Node', 'Remote Node'], reference=reference,
validation_state="validated")[0].UID
def getComputePartitionObject(self, compute_node_reference,
compute_partition_reference):
......@@ -95,28 +92,3 @@ class SlapOSCatalogToolCacheMixin(object):
(compute_node_reference, compute_partition_reference))
else:
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 @@
</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>SlapOSCatalogToolCacheMixin</string> </value>
......@@ -61,28 +55,13 @@
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
......@@ -95,7 +74,7 @@
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
......@@ -104,7 +83,7 @@
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
......
......@@ -115,7 +115,9 @@ class SlapOSComputeNodeMixin(object):
dict (
time=time.time(),
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\
.getRamCacheRoot().get('compute_node_information_cache_factory'\
......@@ -314,7 +316,7 @@ class SlapOSComputeNodeMixin(object):
return partition_dict
def getSoftwareInstallationFromUrl(self, url):
def _getSoftwareInstallationFromUrl(self, url):
software_installation_list = self.getPortalObject().portal_catalog.unrestrictedSearchResults(
portal_type='Software Installation',
default_aggregate_uid=self.getUid(),
......@@ -334,70 +336,3 @@ class SlapOSComputeNodeMixin(object):
self.getReference(), ', '.join([q.getRelativeUrl() for q \
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:
class SlapOSComputePartitionMixin(object):
def getSoftwareInstance(self, slave_reference=None):
def _getSoftwareInstance(self, slave_reference=None):
if self.getSlapState() != 'busy':
LOG('SlapOSComputePartitionMixin::_getSoftwareInstance', INFO,
'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 @@
<tuple/>
</value>
</item>
<item>
<key> <string>type_mixin</string> </key>
<value>
<tuple>
<string>SlapOSInstanceTreeMixin</string>
</tuple>
</value>
</item>
</dictionary>
</pickle>
</record>
......
......@@ -102,15 +102,6 @@
<tuple/>
</value>
</item>
<item>
<key> <string>type_mixin</string> </key>
<value>
<tuple>
<string>JIOAPIRevisionMixin</string>
<string>SlapOSCacheMixin</string>
</tuple>
</value>
</item>
</dictionary>
</pickle>
</record>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="JSON Type" module="erp5.portal_type"/>
<global name="Base Type" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......@@ -18,6 +18,10 @@
<none/>
</value>
</item>
<item>
<key> <string>factory</string> </key>
<value> <string>addXMLObject</string> </value>
</item>
<item>
<key> <string>group_list</string> </key>
<value>
......@@ -44,61 +48,22 @@
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>JSON Type</string> </value>
<value> <string>Base Type</string> </value>
</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 Installation",\n
"description": "Software Installation",\n
"type": "object",\n
"properties": {\n
"software_release_uri": {\n
"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>
<key> <string>searchable_text_property_id</string> </key>
<value>
<tuple>
<string>title</string>
<string>description</string>
<string>reference</string>
<string>short_title</string>
</tuple>
</value>
</item>
<item>
<key> <string>type_class</string> </key>
<value> <string>SoftwareInstallation</string> </value>
<value> <string>Item</string> </value>
</item>
<item>
<key> <string>type_interface</string> </key>
......@@ -106,15 +71,6 @@
<tuple/>
</value>
</item>
<item>
<key> <string>type_mixin</string> </key>
<value>
<tuple>
<string>JIOAPIRevisionMixin</string>
<string>SlapOSCacheMixin</string>
</tuple>
</value>
</item>
</dictionary>
</pickle>
</record>
......
......@@ -2,16 +2,29 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="JSON Type" module="erp5.portal_type"/>
<global name="Base Type" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_property_domain_dict</string> </key>
<value>
<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>
</item>
<item>
<key> <string>content_icon</string> </key>
<value> <string>document.gif</string> </value>
......@@ -20,6 +33,10 @@
<key> <string>description</string> </key>
<value> <string>ERP5 default document. Supports synchronisation and XML.</string> </value>
</item>
<item>
<key> <string>factory</string> </key>
<value> <string>addSoftwareInstance</string> </value>
</item>
<item>
<key> <string>group_list</string> </key>
<value>
......@@ -28,14 +45,26 @@
</tuple>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value> <string>document_icon.gif</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Software Instance</string> </value>
</item>
<item>
<key> <string>immediate_view</string> </key>
<value> <string>XMLObject_view</string> </value>
</item>
<item>
<key> <string>init_script</string> </key>
<value> <string>SoftwareInstance_init</string> </value>
</item>
<item>
<key> <string>meta_type</string> </key>
<value> <string>ERP5 XML Object</string> </value>
</item>
<item>
<key> <string>permission</string> </key>
<value>
......@@ -44,7 +73,11 @@
</item>
<item>
<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>
<key> <string>searchable_text_property_id</string> </key>
......@@ -57,159 +90,6 @@
</tuple>
</value>
</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>
<key> <string>type_class</string> </key>
<value> <string>SoftwareInstance</string> </value>
......@@ -220,72 +100,10 @@
<tuple/>
</value>
</item>
<item>
<key> <string>type_mixin</string> </key>
<value>
<tuple>
<string>JIOAPIRevisionMixin</string>
<string>SlapOSCacheMixin</string>
</tuple>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>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>
<global name="TranslationInformation" module="Products.ERP5Type.TranslationProviderBase"/>
</pickle>
......@@ -302,7 +120,7 @@
</dictionary>
</pickle>
</record>
<record id="5" aka="AAAAAAAAAAU=">
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="TranslationInformation" module="Products.ERP5Type.TranslationProviderBase"/>
</pickle>
......
......@@ -3,7 +3,6 @@
<item>SlapOSCatalogToolCacheMixin</item>
</portal_type>
<portal_type id="Compute Node">
<item>JIOAPIRevisionMixin</item>
<item>SlapOSCacheMixin</item>
<item>SlapOSComputeNodeMixin</item>
</portal_type>
......@@ -11,8 +10,8 @@
<item>SlapOSCacheMixin</item>
<item>SlapOSComputePartitionMixin</item>
</portal_type>
<portal_type id="Instance Tree">
<item>SlapOSInstanceTreeMixin</item>
<portal_type id="Instance Node">
<item>SlapOSCacheMixin</item>
</portal_type>
<portal_type id="Person">
<item>SlapOSCacheMixin</item>
......@@ -28,12 +27,5 @@
</portal_type>
<portal_type id="Software Instance">
<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>
</type_mixin>
\ No newline at end of file
......@@ -100,7 +100,6 @@
<value>
<list>
<string>my_preferred_hateoas_url</string>
<string>my_preferred_jio_api_url</string>
<string>my_preferred_slapos_web_site_url</string>
<string>my_preferred_shacache_website_expected_state</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 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Document Component" module="erp5.portal_type"/>
<global name="Test Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>default_reference</string> </key>
<value> <string>SoftwareInstallation</string> </value>
</item>
<item>
<key> <string>default_source_reference</string> </key>
<value>
<none/>
</value>
<value> <string>SlapOSTestCaseDefaultScenarioMixin</string> </value>
</item>
<item>
<key> <string>description</string> </key>
......@@ -24,11 +18,11 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>document.erp5.SoftwareInstallation</string> </value>
<value> <string>test.erp5.SlapOSTestCaseDefaultScenarioMixin</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Document Component</string> </value>
<value> <string>Test Component</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
......
......@@ -40,7 +40,7 @@ compute_node_list = portal.portal_catalog.portal_catalog(
if len(compute_node_list) == 2:
raise NotImplementedError
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()
else:
......@@ -57,6 +57,9 @@ else:
)
compute_node.approveComputeNodeRegistration()
compute_node = context.restrictedTraverse(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_reference", compute_node.getReference())
erp5_json_editor
erp5_access_token
erp5_api_style
erp5_item
erp5_computer_immobilisation
erp5_software_pdm
......
document.erp5.SoftwareInstance
\ No newline at end of file
document.erp5.SoftwareInstallation
\ No newline at end of file
......@@ -2,4 +2,3 @@ mixin.erp5.SlapOSCacheMixin
mixin.erp5.SlapOSComputeNodeMixin
mixin.erp5.SlapOSComputePartitionMixin
mixin.erp5.SlapOSCatalogToolCacheMixin
\ No newline at end of file
mixin.erp5.SlapOSInstanceTreeMixin
\ No newline at end of file
Catalog Tool | SlapOSCatalogToolCacheMixin
Compute Node | JIOAPIRevisionMixin
Compute Node | SlapOSCacheMixin
Compute Node | SlapOSComputeNodeMixin
Compute Partition | SlapOSCacheMixin
......@@ -8,9 +7,5 @@ Instance Node | SlapOSCacheMixin
Person | SlapOSCacheMixin
Remote Node | SlapOSCacheMixin
Slave Instance | SlapOSCacheMixin
Software Instance | SlapOSCacheMixin
Software Installation | SlapOSCacheMixin
Instance Tree | SlapOSInstanceTreeMixin
Slave Instance | JIOAPIRevisionMixin
Software Instance | JIOAPIRevisionMixin
Software Installation | JIOAPIRevisionMixin
Software Instance | SlapOSCacheMixin
\ No newline at end of file
......@@ -14,5 +14,4 @@ test.erp5.testSlapOSCloudPersonSlapInterfaceWorkflow
test.erp5.testSlapOSCloudProjectSlapInterfaceWorkflow
test.erp5.testSlapOSCloudSecurityGroup
test.erp5.testSlapOSCloudShadow
test.erp5.SlapOSTestCaseMixin
test.erp5.testSlapOSCloud
\ No newline at end of file
test.erp5.testSlapOSCloudUpgrader
\ 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