Commit 032ab585 authored by Rafael Monnerat's avatar Rafael Monnerat

slapos_cloud: Update constraints to prevent set improper data on request

   Try to guarantee that data is consistent and wont fail later on
   while been converted to XML with special attention to non-XML
   compatibles characters, like backspace.

   Include constraint to ensure title is XML compatible on Software
   Instance, Instance Tree and Slave Instance. (Not required for Compute
   Node as far as I checked).

   Copy text_content (parameter), sla_xml constraints from
   Software Instance to Instance Tree and Slave Instance.

   Copy connection_xml constraint from Software to Slave Instance for
   consistency.

   Add more tests to ensure that an Instance cannot be created if data
   is invalid, or not XML compatible.

   Add utility scripts to validate XML and XML Marshaller compatibility
parent d4d2fe4b
Pipeline #37720 failed with stage
in 0 seconds
...@@ -34,7 +34,8 @@ from zExceptions import Unauthorized ...@@ -34,7 +34,8 @@ from zExceptions import Unauthorized
from DateTime import DateTime from DateTime import DateTime
from Products.ERP5Type.UnrestrictedMethod import UnrestrictedMethod from Products.ERP5Type.UnrestrictedMethod import UnrestrictedMethod
from Acquisition import aq_base, aq_inner from Acquisition import aq_base, aq_inner
from slapos.util import dumps
from lxml import etree
def cloneDocumentWithANewPortalType(obj, portal_type): def cloneDocumentWithANewPortalType(obj, portal_type):
import erp5.portal_type import erp5.portal_type
...@@ -324,7 +325,7 @@ def Base_updateRelatedContentWithoutReindextion(self, previous_category_url, new ...@@ -324,7 +325,7 @@ def Base_updateRelatedContentWithoutReindextion(self, previous_category_url, new
previous_category_url, previous_category_url,
new_category_url) new_category_url)
membership_list.append(new_category) membership_list.append(new_category)
# I'm preserving reindexation here in case, since I'm not entire sure the # I'm preserving reindexation here in case, since I'm not entire sure the
# Impact # Impact
predicate.edit(membership_criterion_category_list=membership_list, predicate.edit(membership_criterion_category_list=membership_list,
...@@ -345,3 +346,17 @@ def Base_updateRelatedContentWithoutReindextion(self, previous_category_url, new ...@@ -345,3 +346,17 @@ def Base_updateRelatedContentWithoutReindextion(self, previous_category_url, new
o.Base_updateRelatedContentWithoutReindextion(previous_o_category_url, o.Base_updateRelatedContentWithoutReindextion(previous_o_category_url,
new_o_category_url) new_o_category_url)
def isValidXml(self, value, REQUEST=None):
if REQUEST is not None:
raise Unauthorized
# No better way them this for now
etree.fromstring(value)
return True
def isValidXmlMarshaller(self, value, REQUEST=None):
if REQUEST is not None:
raise Unauthorized
# No better way them this for now
dumps(value)
return True
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="TALES Constraint" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_identity_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_range_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>expression</string> </key>
<value> <string>python: context.Base_isValidXml(context.getSlaXml())</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>sla_xml_valid_xml_constraint</string> </value>
</item>
<item>
<key> <string>int_index</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>membership_criterion_category</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>message_expression_error</string> </key>
<value> <string>Sla XML is invalid: ${error}</string> </value>
</item>
<item>
<key> <string>message_expression_false</string> </key>
<value> <string>Sla XML expression was false</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>TALES Constraint</string> </value>
</item>
<item>
<key> <string>string_index</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>test_method_id</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>test_tales_expression</string> </key>
<value> <string>python: context.getSlaXml() is not None</string> </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/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="TALES Constraint" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_identity_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_range_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>expression</string> </key>
<value> <string>python: context.Base_isValidXml(context.getTextContent())</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>text_content_valid_xml_constraint</string> </value>
</item>
<item>
<key> <string>int_index</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>membership_criterion_category</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>message_expression_error</string> </key>
<value> <string>Instance XML is invalid: ${error}</string> </value>
</item>
<item>
<key> <string>message_expression_false</string> </key>
<value> <string>Instance XML expression was false</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>TALES Constraint</string> </value>
</item>
<item>
<key> <string>string_index</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>test_method_id</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>test_tales_expression</string> </key>
<value> <string>python: context.getTextContent() is not None</string> </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/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="TALES Constraint" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_identity_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_range_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>expression</string> </key>
<value> <string>python: context.Base_isValidXmlMarshaller(context.getTitle())</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>title_xml_marshaller_constraint</string> </value>
</item>
<item>
<key> <string>int_index</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>membership_criterion_category</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>message_expression_error</string> </key>
<value> <string>Title is invalid: ${error}</string> </value>
</item>
<item>
<key> <string>message_expression_false</string> </key>
<value> <string>Title expression was false</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>TALES Constraint</string> </value>
</item>
<item>
<key> <string>string_index</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>test_method_id</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>test_tales_expression</string> </key>
<value> <string>python: context.getTextContent() is not None</string> </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/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="TALES Constraint" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_identity_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_range_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>expression</string> </key>
<value> <string>python: context.getConnectionXmlAsDict() or True</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>connection_xml_valid_xml_constraint</string> </value>
</item>
<item>
<key> <string>int_index</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>membership_criterion_category</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>message_expression_error</string> </key>
<value> <string>Connection XML is invalid: ${error}</string> </value>
</item>
<item>
<key> <string>message_expression_false</string> </key>
<value> <string>Connection XML expression was false</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>TALES Constraint</string> </value>
</item>
<item>
<key> <string>string_index</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>test_method_id</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>test_tales_expression</string> </key>
<value> <string>python: context.getConnectionXml() is not None</string> </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/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="TALES Constraint" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_identity_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_range_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>expression</string> </key>
<value> <string>python: context.getSlaXmlAsDict() or True</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>sla_xml_valid_xml_constraint</string> </value>
</item>
<item>
<key> <string>int_index</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>membership_criterion_category</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>message_expression_error</string> </key>
<value> <string>Sla XML is invalid: ${error}</string> </value>
</item>
<item>
<key> <string>message_expression_false</string> </key>
<value> <string>Sla XML expression was false</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>TALES Constraint</string> </value>
</item>
<item>
<key> <string>string_index</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>test_method_id</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>test_tales_expression</string> </key>
<value> <string>python: context.getSlaXml() is not None</string> </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/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="TALES Constraint" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_identity_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_range_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>expression</string> </key>
<value> <string>python: context.getInstanceXmlAsDict() or True</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>text_content_valid_xml_constraint</string> </value>
</item>
<item>
<key> <string>int_index</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>membership_criterion_category</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>message_expression_error</string> </key>
<value> <string>Instance XML is invalid: ${error}</string> </value>
</item>
<item>
<key> <string>message_expression_false</string> </key>
<value> <string>Instance XML expression was false</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>TALES Constraint</string> </value>
</item>
<item>
<key> <string>string_index</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>test_method_id</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>test_tales_expression</string> </key>
<value> <string>python: context.getTextContent() is not None</string> </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/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="TALES Constraint" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_identity_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_range_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>expression</string> </key>
<value> <string>python: context.Base_isValidXmlMarshaller(context.getTitle())</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>title_xml_marshaller_constraint</string> </value>
</item>
<item>
<key> <string>int_index</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>membership_criterion_category</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>message_expression_error</string> </key>
<value> <string>Title is invalid: ${error}</string> </value>
</item>
<item>
<key> <string>message_expression_false</string> </key>
<value> <string>Title expression was false</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>TALES Constraint</string> </value>
</item>
<item>
<key> <string>string_index</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>test_method_id</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>test_tales_expression</string> </key>
<value> <string>python: context.getTextContent() is not None</string> </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/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="TALES Constraint" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_identity_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_range_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>expression</string> </key>
<value> <string>python: context.Base_isValidXmlMarshaller(context.getTitle())</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>title_xml_marshaller_constraint</string> </value>
</item>
<item>
<key> <string>int_index</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>membership_criterion_category</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>message_expression_error</string> </key>
<value> <string>Title is invalid: ${error}</string> </value>
</item>
<item>
<key> <string>message_expression_false</string> </key>
<value> <string>Title expression was false</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>TALES Constraint</string> </value>
</item>
<item>
<key> <string>string_index</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>test_method_id</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>test_tales_expression</string> </key>
<value> <string>python: context.getTextContent() is not None</string> </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/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ExternalMethod" module="Products.ExternalMethod.ExternalMethod"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_function</string> </key>
<value> <string>isValidXml</string> </value>
</item>
<item>
<key> <string>_module</string> </key>
<value> <string>SlapOSCloud</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Base_isValidXml</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ExternalMethod" module="Products.ExternalMethod.ExternalMethod"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_function</string> </key>
<value> <string>isValidXmlMarshaller</string> </value>
</item>
<item>
<key> <string>_module</string> </key>
<value> <string>SlapOSCloud</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Base_isValidXmlMarshaller</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
...@@ -26,21 +26,29 @@ ...@@ -26,21 +26,29 @@
# #
############################################################################## ##############################################################################
from Products.ERP5Type.Core.Workflow import ValidationFailed
def _decode_with_json(value): from erp5.component.test.SlapOSTestCaseMixin import SlapOSTestCaseMixin, \
# Ensure value is serisalisable as json TemporaryAlarmScript, \
return json.loads(json.dumps(value)) SlapOSTestCaseMixinWithAbort
from erp5.component.test.SlapOSTestCaseMixin import SlapOSTestCaseMixin
from DateTime import DateTime from DateTime import DateTime
from App.Common import rfc1123_date from App.Common import rfc1123_date
import json import json
import hashlib import hashlib
from binascii import hexlify from binascii import hexlify
from lxml import etree
import transaction
BACKSPACE_CHAR = "\x08"
BELL_CHAR = "\x07"
HORIZONTAL_TAB_CHAR = "\x09"
def hashData(data): def hashData(data):
return hexlify(hashlib.sha1(json.dumps(data, sort_keys=True)).digest()) return hexlify(hashlib.sha1(json.dumps(data, sort_keys=True)).digest())
def _decode_with_json(value):
# Ensure value is serisalisable as json
return json.loads(json.dumps(value))
class TestSlapOSCloudSlapOSCacheMixin( class TestSlapOSCloudSlapOSCacheMixin(
SlapOSTestCaseMixin): SlapOSTestCaseMixin):
...@@ -81,7 +89,7 @@ class TestSlapOSCloudSlapOSCacheMixin( ...@@ -81,7 +89,7 @@ class TestSlapOSCloudSlapOSCacheMixin(
"text": "#error no data found for %s" % doc.getReference(), "text": "#error no data found for %s" % doc.getReference(),
"no_data": 1 "no_data": 1
} }
# Check Compute Node # Check Compute Node
self.assertEqual(self.compute_node._getCachedAccessInfo(), None) self.assertEqual(self.compute_node._getCachedAccessInfo(), None)
self.assertEqual(self.compute_node.getAccessStatus(), self.assertEqual(self.compute_node.getAccessStatus(),
...@@ -131,7 +139,7 @@ class TestSlapOSCloudSlapOSCacheMixin( ...@@ -131,7 +139,7 @@ class TestSlapOSCloudSlapOSCacheMixin(
'no_data_since_15_minutes': 0, 'no_data_since_15_minutes': 0,
'no_data_since_5_minutes': 0 'no_data_since_5_minutes': 0
}) })
# Check Compute Node # Check Compute Node
self.assertEqual(True, self.assertEqual(True,
self.compute_node.setAccessStatus("TEST123 %s" % self.compute_node.getUid())) self.compute_node.setAccessStatus("TEST123 %s" % self.compute_node.getUid()))
...@@ -205,7 +213,7 @@ class TestSlapOSCloudSlapOSCacheMixin( ...@@ -205,7 +213,7 @@ class TestSlapOSCloudSlapOSCacheMixin(
'no_data_since_15_minutes': 0, 'no_data_since_15_minutes': 0,
'no_data_since_5_minutes': 0 'no_data_since_5_minutes': 0
}) })
self.tic() self.tic()
...@@ -215,7 +223,7 @@ class TestSlapOSCloudSlapOSCacheMixin( ...@@ -215,7 +223,7 @@ class TestSlapOSCloudSlapOSCacheMixin(
uid=instance.getUid(), uid=instance.getUid(),
select_dict={"indexation_timestamp": None} select_dict={"indexation_timestamp": None}
)[0].indexation_timestamp )[0].indexation_timestamp
# This is already called from elsewhere, so it actually changed # This is already called from elsewhere, so it actually changed
self.assertEqual(True, self.assertEqual(True,
instance.setAccessStatus("TEST123 %s" % instance.getUid())) instance.setAccessStatus("TEST123 %s" % instance.getUid()))
...@@ -248,7 +256,7 @@ class TestSlapOSCloudSlapOSCacheMixin( ...@@ -248,7 +256,7 @@ class TestSlapOSCloudSlapOSCacheMixin(
select_dict={"indexation_timestamp": None} select_dict={"indexation_timestamp": None}
)[0].indexation_timestamp )[0].indexation_timestamp
self.assertEqual(new_indexation_timestamp, indexation_timestamp) self.assertEqual(new_indexation_timestamp, indexation_timestamp)
self.assertEqual(True, self.assertEqual(True,
instance.setErrorStatus("TEST123 %s" % instance.getUid(), reindex=1)) instance.setErrorStatus("TEST123 %s" % instance.getUid(), reindex=1))
self.tic() self.tic()
...@@ -287,7 +295,7 @@ class TestSlapOSCloudSlapOSCacheMixin( ...@@ -287,7 +295,7 @@ class TestSlapOSCloudSlapOSCacheMixin(
'no_data_since_15_minutes': 0, 'no_data_since_15_minutes': 0,
'no_data_since_5_minutes': 0 'no_data_since_5_minutes': 0
}) })
# Check Compute Node # Check Compute Node
self.assertEqual(True, self.assertEqual(True,
self.compute_node.setErrorStatus("TEST123 %s" % self.compute_node.getUid())) self.compute_node.setErrorStatus("TEST123 %s" % self.compute_node.getUid()))
...@@ -368,17 +376,67 @@ class TestSlapOSCloudSlapOSCacheMixin( ...@@ -368,17 +376,67 @@ class TestSlapOSCloudSlapOSCacheMixin(
self.assertEqual(installation._getCachedAccessInfo(), self.assertEqual(installation._getCachedAccessInfo(),
getExpectedCacheDict(installation)) getExpectedCacheDict(installation))
self.assertEqual(_decode_with_json(installation.getAccessStatus()), self.assertEqual(_decode_with_json(installation.getAccessStatus()),
getBaseExpectedDict(installation)) getBaseExpectedDict(installation))
self.assertEqual(False, self.assertEqual(False,
installation.setBuildingStatus("TEST123 %s" % installation.getUid())) installation.setBuildingStatus("TEST123 %s" % installation.getUid()))
class TestBase_isValidXmlMarshaller(SlapOSTestCaseMixinWithAbort):
def test_Base_isValidXmlMarshaller(self):
self.assertTrue(self.portal.Base_isValidXmlMarshaller("""<instance/>"""))
self.assertTrue(self.portal.Base_isValidXmlMarshaller("""<instance></instance>"""))
self.assertTrue(self.portal.Base_isValidXmlMarshaller("""<instance>"""))
self.assertTrue(self.portal.Base_isValidXmlMarshaller(""))
self.assertTrue(self.portal.Base_isValidXmlMarshaller("""<insta"""))
self.assertTrue(self.portal.Base_isValidXmlMarshaller({HORIZONTAL_TAB_CHAR: 1}))
self.assertTrue(self.portal.Base_isValidXmlMarshaller([HORIZONTAL_TAB_CHAR, 1]))
self.assertTrue(self.portal.Base_isValidXmlMarshaller(HORIZONTAL_TAB_CHAR))
self.assertTrue(self.portal.Base_isValidXmlMarshaller({'zz': {'aa'}}))
self.assertTrue(self.portal.Base_isValidXmlMarshaller({}))
self.assertTrue(self.portal.Base_isValidXmlMarshaller(1))
self.assertRaises(ValueError,
self.portal.Base_isValidXmlMarshaller,
"<instance>%s</instance>" % BACKSPACE_CHAR)
self.assertRaises(ValueError,
self.portal.Base_isValidXmlMarshaller, BACKSPACE_CHAR)
self.assertRaises(ValueError,
self.portal.Base_isValidXmlMarshaller, {BACKSPACE_CHAR: 1})
self.assertRaises(ValueError,
self.portal.Base_isValidXmlMarshaller, [BACKSPACE_CHAR, 1])
self.assertRaises(ValueError,
self.portal.Base_isValidXmlMarshaller, BELL_CHAR)
self.assertRaises(ValueError,
self.portal.Base_isValidXmlMarshaller, {BELL_CHAR: 1})
self.assertRaises(ValueError,
self.portal.Base_isValidXmlMarshaller, [BELL_CHAR, 1])
class TestBase_isValidXml(SlapOSTestCaseMixinWithAbort):
def test_Base_isValidXml(self):
self.assertTrue(self.portal.Base_isValidXml("<instance/>"))
self.assertTrue(self.portal.Base_isValidXml("<instance></instance>"))
self.assertTrue(
self.portal.Base_isValidXml("<instance>%s</instance>" % HORIZONTAL_TAB_CHAR))
self.assertRaises(etree.XMLSyntaxError, self.portal.Base_isValidXml, "<instance>")
self.assertRaises(etree.XMLSyntaxError, self.portal.Base_isValidXml, "")
self.assertRaises(ValueError, self.portal.Base_isValidXml, None)
self.assertRaises(etree.XMLSyntaxError, self.portal.Base_isValidXml, "<insta")
self.assertRaises(etree.XMLSyntaxError, self.portal.Base_isValidXml,
"<instance>%s</instance>" % BACKSPACE_CHAR)
class TestSlapOSCloudSoftwareInstance( class TestSlapOSCloudSoftwareInstance(
SlapOSTestCaseMixin): SlapOSTestCaseMixin):
def afterSetUp(self): def afterSetUp(self):
SlapOSTestCaseMixin.afterSetUp(self) SlapOSTestCaseMixin.afterSetUp(self)
self.project = self.addProject() self.project = self.addProject()
self._makeTree(self.project) self.tic()
self.instance_tree = self.addInstanceTree(self.project)
self.software_instance = self.instance_tree.getSuccessorValue()
self.tic()
def test_getXmlAsDict(self): def test_getXmlAsDict(self):
simple_parameter_sample_xml = """<?xml version='1.0' encoding='utf-8'?> simple_parameter_sample_xml = """<?xml version='1.0' encoding='utf-8'?>
...@@ -391,6 +449,16 @@ class TestSlapOSCloudSoftwareInstance( ...@@ -391,6 +449,16 @@ class TestSlapOSCloudSoftwareInstance(
self.software_instance._getXmlAsDict(simple_parameter_sample_xml), self.software_instance._getXmlAsDict(simple_parameter_sample_xml),
{'p1é': 'v1é', 'p2é': 'v2é'}) {'p1é': 'v1é', 'p2é': 'v2é'})
def test_getXmlAsDict_invalid_backspace_char(self):
simple_parameter_sample_xml = """<?xml version='1.0' encoding='utf-8'?>
<instance>
<parameter id="p1é%(bs)s">v1é%(bs)s</parameter>
<parameter id="p2é%(bs)s">v2é%(bs)s</parameter>
</instance>
""" % {'bs' : BACKSPACE_CHAR}
self.assertRaises(etree.XMLSyntaxError,
self.software_instance._getXmlAsDict, simple_parameter_sample_xml)
def test_getInstanceXmlAsDict(self): def test_getInstanceXmlAsDict(self):
self.software_instance.setTextContent("""<?xml version='1.0' encoding='utf-8'?> self.software_instance.setTextContent("""<?xml version='1.0' encoding='utf-8'?>
<instance> <instance>
...@@ -402,6 +470,26 @@ class TestSlapOSCloudSoftwareInstance( ...@@ -402,6 +470,26 @@ class TestSlapOSCloudSoftwareInstance(
self.software_instance.getInstanceXmlAsDict(), self.software_instance.getInstanceXmlAsDict(),
{'p1é': 'v1é', 'p2é': 'v2é'}) {'p1é': 'v1é', 'p2é': 'v2é'})
# Ensure SoftwareInstanceConstraint catches eventual the problems.
self.assertEqual(self.software_instance.checkConsistency(), [])
def test_getInstanceXmlAsDict_invalid_backspace_char(self):
self.software_instance.setTextContent("""<?xml version='1.0' encoding='utf-8'?>
<instance>
<parameter id="p1é%(bs)s">v1é</parameter>
<parameter id="p2é">v2é%(bs)s</parameter>
</instance>
""" % {'bs' : BACKSPACE_CHAR})
self.assertRaises(etree.XMLSyntaxError,
self.software_instance.getInstanceXmlAsDict)
# Ensure SoftwareInstanceConstraint catches eventual the problems.
consistency_list = self.software_instance.checkConsistency()
self.assertEqual(len(consistency_list), 1, consistency_list)
self.assertTrue(
str(consistency_list[0].getTranslatedMessage()).startswith(
'Instance XML is invalid: invalid character'), consistency_list)
def test_getSlaXmlAsDict(self): def test_getSlaXmlAsDict(self):
self.software_instance.setSlaXml("""<?xml version='1.0' encoding='utf-8'?> self.software_instance.setSlaXml("""<?xml version='1.0' encoding='utf-8'?>
<instance> <instance>
...@@ -413,6 +501,26 @@ class TestSlapOSCloudSoftwareInstance( ...@@ -413,6 +501,26 @@ class TestSlapOSCloudSoftwareInstance(
self.software_instance.getSlaXmlAsDict(), self.software_instance.getSlaXmlAsDict(),
{'p1é': 'v1é', 'p2é': 'v2é'}) {'p1é': 'v1é', 'p2é': 'v2é'})
# Ensure SoftwareInstanceConstraint catches eventual the problems.
self.assertEqual(self.software_instance.checkConsistency(), [])
def test_getSlaXmlAsDict_invalid_backspace_char(self):
self.software_instance.setSlaXml("""<?xml version='1.0' encoding='utf-8'?>
<instance>
<parameter id="p1é">v1é</parameter>
<parameter id="p2é">v2é%(bs)s</parameter>
</instance>
""" % {'bs' : BACKSPACE_CHAR})
self.assertRaises(etree.XMLSyntaxError,
self.software_instance.getSlaXmlAsDict)
# Ensure SoftwareInstanceConstraint catches eventual the problems.
consistency_list = self.software_instance.checkConsistency()
self.assertEqual(len(consistency_list), 1, consistency_list)
self.assertTrue(
str(consistency_list[0].getTranslatedMessage()).startswith(
'Sla XML is invalid: PCDATA invalid Char value'), consistency_list)
def test_getConnectionXmlAsDict(self): def test_getConnectionXmlAsDict(self):
self.software_instance.setConnectionXml("""<?xml version='1.0' encoding='utf-8'?> self.software_instance.setConnectionXml("""<?xml version='1.0' encoding='utf-8'?>
<instance> <instance>
...@@ -424,6 +532,38 @@ class TestSlapOSCloudSoftwareInstance( ...@@ -424,6 +532,38 @@ class TestSlapOSCloudSoftwareInstance(
self.software_instance.getConnectionXmlAsDict(), self.software_instance.getConnectionXmlAsDict(),
{'p1é': 'v1é', 'p2é': 'v2é'}) {'p1é': 'v1é', 'p2é': 'v2é'})
# Ensure SoftwareInstanceConstraint catches eventual the problems.
self.assertEqual(self.software_instance.checkConsistency(), [])
def test_getConnectionXmlAsDict_invalid_backspace_char(self):
self.software_instance.setConnectionXml("""<?xml version='1.0' encoding='utf-8'?>
<instance>
<parameter id="p1é">v1é</parameter>
<parameter id="p2é">v2é%(bs)s</parameter>
</instance>
""" % {'bs' : BACKSPACE_CHAR})
self.assertRaises(etree.XMLSyntaxError,
self.software_instance.getConnectionXmlAsDict)
# Ensure SoftwareInstanceConstraint catches eventual the problems.
consistency_list = self.software_instance.checkConsistency()
self.assertEqual(len(consistency_list), 1, consistency_list)
self.assertTrue(
str(consistency_list[0].getTranslatedMessage()).startswith(
'Connection XML is invalid: PCDATA invalid Char value'),
consistency_list)
def test_getTitle_invalid_backspace_char(self):
self.software_instance.setTitle("v2é%(bs)s" % ({'bs' : BACKSPACE_CHAR}))
# Ensure SoftwareInstanceConstraint catches eventual the problems.
consistency_list = self.software_instance.checkConsistency()
self.assertEqual(len(consistency_list), 1, consistency_list)
self.assertTrue(
str(consistency_list[0].getTranslatedMessage()).startswith(
'Title is invalid: All strings must be XML compatible'),
consistency_list)
def test_instanceXmlToDict(self): def test_instanceXmlToDict(self):
simple_parameter_sample_xml = """<?xml version='1.0' encoding='utf-8'?> simple_parameter_sample_xml = """<?xml version='1.0' encoding='utf-8'?>
<instance> <instance>
...@@ -436,6 +576,28 @@ class TestSlapOSCloudSoftwareInstance( ...@@ -436,6 +576,28 @@ class TestSlapOSCloudSoftwareInstance(
# different from getXmlAsDict it don't encode things as utf-8 # different from getXmlAsDict it don't encode things as utf-8
{u'p1é': u'v1é', u'p2é': u'v2é'}) {u'p1é': u'v1é', u'p2é': u'v2é'})
def test_instanceXmlToDict_invalid_xml(self):
simple_parameter_sample_xml = """<?xml version='1.0' encoding='utf-8'?>
<instance>
<parameter id="p1é">v1é</parameter>
<parameter id="p2é">v2é%(bs)s</parameter>
</instance>
""" % {'bs' : BACKSPACE_CHAR}
self.assertEqual(
self.software_instance._instanceXmlToDict(simple_parameter_sample_xml),
# if the XML is invalid, it return {} by default
{})
simple_parameter_sample_xml = """<?xml version='1.0' encoding='utf-8'?>
<instance>
<parameter id="p1é">v1é</parameter>
<parameter id="p2é">v2é%(bs)s</parameter>
""" % {'bs' : BACKSPACE_CHAR}
self.assertEqual(
self.software_instance._instanceXmlToDict(simple_parameter_sample_xml),
# This dont raise because it isn't converted verified converted as XML.
{})
def test_asParameterDict_not_allocated(self): def test_asParameterDict_not_allocated(self):
self.assertRaises(ValueError, self.assertRaises(ValueError,
self.software_instance._asParameterDict) self.software_instance._asParameterDict)
...@@ -463,11 +625,11 @@ class TestSlapOSCloudSoftwareInstance( ...@@ -463,11 +625,11 @@ class TestSlapOSCloudSoftwareInstance(
self.start_requested_software_instance.getConnectionXml() ) self.start_requested_software_instance.getConnectionXml() )
self.assertEqual(as_parameter_dict["filter_xml"], self.assertEqual(as_parameter_dict["filter_xml"],
self.start_requested_software_instance.getSlaXml() ) self.start_requested_software_instance.getSlaXml() )
self.assertEqual(as_parameter_dict["root_instance_title"], self.assertEqual(as_parameter_dict["root_instance_title"],
self.start_requested_software_instance.getSpecialiseTitle().decode("UTF-8")) self.start_requested_software_instance.getSpecialiseTitle().decode("UTF-8"))
self.assertEqual(as_parameter_dict["root_instance_short_title"], self.assertEqual(as_parameter_dict["root_instance_short_title"],
self.start_requested_software_instance.getSpecialiseShortTitle().decode("UTF-8")) self.start_requested_software_instance.getSpecialiseShortTitle().decode("UTF-8"))
self.assertEqual(as_parameter_dict["slap_computer_id"], self.assertEqual(as_parameter_dict["slap_computer_id"],
self.compute_node.getReference().decode("UTF-8")) self.compute_node.getReference().decode("UTF-8"))
self.assertEqual(as_parameter_dict["slap_computer_partition_id"], self.assertEqual(as_parameter_dict["slap_computer_partition_id"],
"partition1") "partition1")
...@@ -490,10 +652,29 @@ class TestSlapOSCloudSoftwareInstance( ...@@ -490,10 +652,29 @@ class TestSlapOSCloudSoftwareInstance(
self._makeComputeNode(self.project) self._makeComputeNode(self.project)
self._makeComplexComputeNode(self.project, with_slave=True) self._makeComplexComputeNode(self.project, with_slave=True)
self.tic() self.tic()
self.assertEqual([(u'', u'ip_address_1')], self.assertEqual([(u'', u'ip_address_1')],
self.start_requested_software_instance._getInstanceTreeIpList()) self.start_requested_software_instance._getInstanceTreeIpList())
class TestSlapOSCloudSlaveInstance(
TestSlapOSCloudSoftwareInstance):
def _skipTest(self):
pass
test_asParameterDict_not_allocated = _skipTest
test_getInstanceTreeIpList = _skipTest
test_asParameterDict = _skipTest
def afterSetUp(self):
SlapOSTestCaseMixin.afterSetUp(self)
self.project = self.addProject()
self.tic()
self.instance_tree = self.addInstanceTree(project=self.project, shared=True)
self.software_instance = self.instance_tree.getSuccessorValue()
self.tic()
class TestSlapOSCloudSlapOSComputeNodeMixin_getCacheComputeNodeInformation( class TestSlapOSCloudSlapOSComputeNodeMixin_getCacheComputeNodeInformation(
SlapOSTestCaseMixin): SlapOSTestCaseMixin):
...@@ -525,7 +706,7 @@ class TestSlapOSCloudSlapOSComputeNodeMixin_getCacheComputeNodeInformation( ...@@ -525,7 +706,7 @@ class TestSlapOSCloudSlapOSComputeNodeMixin_getCacheComputeNodeInformation(
def test_activate_getCacheComputeNodeInformation_first_access(self): def test_activate_getCacheComputeNodeInformation_first_access(self):
# #
# This is a port of # This is a port of
# TestSlapOSSlapToolgetFullComputerInformation.test_activate_getFullComputerInformation_first_access # TestSlapOSSlapToolgetFullComputerInformation.test_activate_getFullComputerInformation_first_access
# #
...@@ -533,13 +714,13 @@ class TestSlapOSCloudSlapOSComputeNodeMixin_getCacheComputeNodeInformation( ...@@ -533,13 +714,13 @@ class TestSlapOSCloudSlapOSComputeNodeMixin_getCacheComputeNodeInformation(
self.portal.REQUEST['disable_isTestRun'] = True self.portal.REQUEST['disable_isTestRun'] = True
self.login(self.compute_node_user_id) self.login(self.compute_node_user_id)
user = self.getPortalObject().portal_membership.getAuthenticatedMember().getUserName() user = self.portal.portal_membership.getAuthenticatedMember().getUserName()
self.compute_node.setAccessStatus(self.compute_node_id) self.compute_node.setAccessStatus(self.compute_node_id)
refresh_etag = self.compute_node._calculateRefreshEtag() refresh_etag = self.compute_node._calculateRefreshEtag()
body, etag = self.compute_node._getComputeNodeInformation(user, refresh_etag) body, etag = self.compute_node._getComputeNodeInformation(user, refresh_etag)
# This tic and second call is to fix indexation ordering while some sub object # This tic and second call is to fix indexation ordering while some sub object
# is created after the etag is computed and stored. # is created after the etag is computed and stored.
self.tic() self.tic()
refresh_etag = self.compute_node._calculateRefreshEtag() refresh_etag = self.compute_node._calculateRefreshEtag()
body, etag = self.compute_node._getComputeNodeInformation(user, refresh_etag) body, etag = self.compute_node._getComputeNodeInformation(user, refresh_etag)
...@@ -568,7 +749,8 @@ class TestSlapOSCloudSlapOSComputeNodeMixin_getCacheComputeNodeInformation( ...@@ -568,7 +749,8 @@ class TestSlapOSCloudSlapOSComputeNodeMixin_getCacheComputeNodeInformation(
self.assertEqual(first_etag, etag) self.assertEqual(first_etag, etag)
self.assertEqual(first_body_fingerprint, hashData(body)) self.assertEqual(first_body_fingerprint, hashData(body))
self.assertEqual(current_activity_count, len(self.portal.portal_activities.getMessageList())) self.assertEqual(current_activity_count,
len(self.portal.portal_activities.getMessageList()))
self.tic() self.tic()
...@@ -579,7 +761,7 @@ class TestSlapOSCloudSlapOSComputeNodeMixin_getCacheComputeNodeInformation( ...@@ -579,7 +761,7 @@ class TestSlapOSCloudSlapOSComputeNodeMixin_getCacheComputeNodeInformation(
refresh_etag = self.compute_node._calculateRefreshEtag() refresh_etag = self.compute_node._calculateRefreshEtag()
body, etag = self.compute_node._getComputeNodeInformation(user, refresh_etag) body, etag = self.compute_node._getComputeNodeInformation(user, refresh_etag)
self.commit() self.commit()
second_etag = self.compute_node._calculateRefreshEtag() second_etag = self.compute_node._calculateRefreshEtag()
second_body_fingerprint = hashData( second_body_fingerprint = hashData(
self.compute_node._getCacheComputeNodeInformation(self.compute_node_id) self.compute_node._getCacheComputeNodeInformation(self.compute_node_id)
...@@ -627,7 +809,8 @@ class TestSlapOSCloudSlapOSComputeNodeMixin_getCacheComputeNodeInformation( ...@@ -627,7 +809,8 @@ class TestSlapOSCloudSlapOSComputeNodeMixin_getCacheComputeNodeInformation(
self.assertEqual(second_etag, etag) self.assertEqual(second_etag, etag)
self.assertEqual(first_body_fingerprint, hashData(body)) self.assertEqual(first_body_fingerprint, hashData(body))
self.assertEqual(current_activity_count, len(self.portal.portal_activities.getMessageList())) self.assertEqual(current_activity_count,
len(self.portal.portal_activities.getMessageList()))
self.tic() self.tic()
...@@ -636,7 +819,7 @@ class TestSlapOSCloudSlapOSComputeNodeMixin_getCacheComputeNodeInformation( ...@@ -636,7 +819,7 @@ class TestSlapOSCloudSlapOSComputeNodeMixin_getCacheComputeNodeInformation(
self.compute_node.setAccessStatus(self.compute_node_id) self.compute_node.setAccessStatus(self.compute_node_id)
refresh_etag = self.compute_node._calculateRefreshEtag() refresh_etag = self.compute_node._calculateRefreshEtag()
body, etag = self.compute_node._getComputeNodeInformation(user, refresh_etag) body, etag = self.compute_node._getComputeNodeInformation(user, refresh_etag)
# On the previous tic, the values can be indexed out of order, so recall after indextion # On the previous tic, the values can be indexed out of order, so recall after indextion
# so the values are propely set. # so the values are propely set.
self.tic() self.tic()
...@@ -676,7 +859,8 @@ class TestSlapOSCloudSlapOSComputeNodeMixin_getCacheComputeNodeInformation( ...@@ -676,7 +859,8 @@ class TestSlapOSCloudSlapOSComputeNodeMixin_getCacheComputeNodeInformation(
self.assertEqual(third_etag, etag) self.assertEqual(third_etag, etag)
self.assertEqual(third_body_fingerprint, hashData(body)) self.assertEqual(third_body_fingerprint, hashData(body))
self.assertEqual(current_activity_count, len(self.portal.portal_activities.getMessageList())) self.assertEqual(current_activity_count,
len(self.portal.portal_activities.getMessageList()))
self.tic() self.tic()
...@@ -692,7 +876,7 @@ class TestSlapOSCloudSlapOSComputeNodeMixin_getCacheComputeNodeInformation( ...@@ -692,7 +876,7 @@ class TestSlapOSCloudSlapOSComputeNodeMixin_getCacheComputeNodeInformation(
# so the values are propely set. # so the values are propely set.
self.tic() self.tic()
body, etag = self.compute_node._getComputeNodeInformation(user, refresh_etag) body, etag = self.compute_node._getComputeNodeInformation(user, refresh_etag)
self.commit() self.commit()
fourth_etag = self.compute_node._calculateRefreshEtag() fourth_etag = self.compute_node._calculateRefreshEtag()
fourth_body_fingerprint = hashData( fourth_body_fingerprint = hashData(
...@@ -705,4 +889,181 @@ class TestSlapOSCloudSlapOSComputeNodeMixin_getCacheComputeNodeInformation( ...@@ -705,4 +889,181 @@ class TestSlapOSCloudSlapOSComputeNodeMixin_getCacheComputeNodeInformation(
self.assertEqual(fourth_body_fingerprint, hashData(body)) self.assertEqual(fourth_body_fingerprint, hashData(body))
self.assertEqual(0, len(self.portal.portal_activities.getMessageList())) self.assertEqual(0, len(self.portal.portal_activities.getMessageList()))
class TestSlapOSCloudInstanceInvalidRequest(SlapOSTestCaseMixin):
def afterSetUp(self):
SlapOSTestCaseMixin.afterSetUp(self)
self.project = self.addProject()
def generateUnsafeXml_bad_string(self):
return '<?xml version="1.0" encoding="utf-8"?><instance><parameter '\
'id="%s">%s\x08</parameter></instance>' % \
("paramé".decode("UTF-8").encode("UTF-8"),
self.generateNewId().decode("UTF-8").encode("UTF-8"))
def _test_invalid(self, title=None, instance_xml=None, sla_xml=None, shared=False):
self._makeComputeNode(self.project)
self._makeComplexComputeNode(self.project, with_slave=not shared)
self.tic()
if title is None:
title = self.generateNewSoftwareTitle()
# Create second Slave with invalid char (backspace)
instance_tree = self.portal.instance_tree_module\
.newContent(portal_type="Instance Tree")
instance_tree.validate()
instance_tree.edit(
title=self.generateNewSoftwareTitle(),
reference="TESTSI-%s" % self.generateNewId(),
# No need
destination_section_value=None,
follow_up_value=self.project
)
if not instance_xml:
instance_xml = self.generateSafeXml()
if not sla_xml:
sla_xml = self.generateSafeXml()
software_instance_kw = dict(
software_release=self.start_requested_software_instance.getUrlString(),
software_type=self.start_requested_software_instance.getSourceReference(),
instance_xml=instance_xml,
sla_xml=sla_xml,
shared=shared,
software_title=title,
state='started',
project_reference=self.project.getReference()
)
# Ensure this produces a consistent instance first, to start.
instance_tree_kw = software_instance_kw.copy()
instance_tree_kw.update({
"instance_xml" : self.generateSafeXml(),
"sla_xml" : self.generateSafeXml(),
"software_title" : instance_tree.getTitle()
})
instance_tree.requestStart(**instance_tree_kw)
with TemporaryAlarmScript(self.portal, 'Item_getSubscriptionStatus', "'subscribed'"):
with self.assertRaises(ValidationFailed):
instance_tree.requestInstance(**software_instance_kw)
# Abort transaction to prevent interaction workflow to trigger,
# and to the temporary script be removed
transaction.abort()
def test_invalid_backspace_char_on_software_title(self):
title = "abc\08x" + self.generateNewSoftwareTitle()
self._test_invalid(title=title)
def test_invalid_backspace_char_on_slave_title(self):
title = "abc\08x" + self.generateNewSoftwareTitle()
self._test_invalid(title=title, shared=True)
def test_invalid_backspace_char_on_software_paramameter_xml(self):
invalid_xml = self.generateUnsafeXml_bad_string()
self._test_invalid(instance_xml=invalid_xml)
def test_invalid_backspace_char_on_slave_paramameter_xml(self):
invalid_xml = self.generateUnsafeXml_bad_string()
self._test_invalid(instance_xml=invalid_xml, shared=True)
def test_invalid_backspace_char_on_software_sla_xml(self):
invalid_xml = self.generateUnsafeXml_bad_string()
self._test_invalid(sla_xml=invalid_xml)
def test_invalid_backspace_char_on_slave_sla_xml(self):
invalid_xml = self.generateUnsafeXml_bad_string()
self._test_invalid(sla_xml=invalid_xml, shared=True)
def _test_invalid_connection_parameter(self, connection_xml, shared=False):
self._makeComputeNode(self.project)
self._makeComplexComputeNode(self.project, with_slave=not shared)
self.tic()
# Create second Slave with invalid char (backspace)
instance_tree = self.portal.instance_tree_module\
.newContent(portal_type="Instance Tree")
instance_tree.validate()
instance_tree.edit(
title=self.generateNewSoftwareTitle(),
reference="TESTSI-%s" % self.generateNewId(),
# No need
destination_section_value=None,
follow_up_value=self.project
)
software_instance_kw = dict(
software_release=self.start_requested_software_instance.getUrlString(),
software_type=self.start_requested_software_instance.getSourceReference(),
instance_xml=self.generateSafeXml(),
sla_xml=self.generateSafeXml(),
shared=shared,
software_title=instance_tree.getTitle(),
state='started',
project_reference=self.project.getReference()
)
instance_tree.requestStart(**software_instance_kw)
with TemporaryAlarmScript(self.portal, 'Item_getSubscriptionStatus', "'subscribed'"):
instance_tree.requestInstance(**software_instance_kw)
software_instance = instance_tree.getSuccessorValue()
with self.assertRaises(ValidationFailed):
software_instance.updateConnection(connection_xml=connection_xml)
def test_invalid_backspace_char_on_software_connection_xml(self):
invalid_xml = self.generateUnsafeXml_bad_string()
self._test_invalid_connection_parameter(invalid_xml)
def test_invalid_backspace_char_on_slave_connection_xml(self):
invalid_xml = self.generateUnsafeXml_bad_string()
self._test_invalid_connection_parameter(invalid_xml, shared=True)
def _test_invalid_instance_tree(self, title=None,
instance_xml=None, sla_xml=None):
self._makeComputeNode(self.project)
self._makeComplexComputeNode(self.project)
self.tic()
if title is None:
title = self.generateNewSoftwareTitle()
# Create second Slave with invalid char (backspace)
instance_tree = self.portal.instance_tree_module\
.newContent(portal_type="Instance Tree")
instance_tree.validate()
instance_tree.edit(
title=title,
reference="TESTSI-%s" % self.generateNewId(),
# No need
destination_section_value=None,
follow_up_value=self.project
)
if not instance_xml:
instance_xml = self.generateSafeXml()
if not sla_xml:
sla_xml = self.generateSafeXml()
instance_tree_kw = dict(
software_release=self.start_requested_software_instance.getUrlString(),
software_type=self.start_requested_software_instance.getSourceReference(),
instance_xml=instance_xml,
sla_xml=sla_xml,
shared=False,
software_title=title,
state='started',
project_reference=self.project.getReference()
)
with self.assertRaises(ValidationFailed):
instance_tree.requestStart(**instance_tree_kw)
def test_invalid_backspace_char_on_instance_title(self):
title = "abc\08x" + self.generateNewSoftwareTitle()
self._test_invalid_instance_tree(title=title)
def test_invalid_backspace_char_on_instance_paramameter_xml(self):
invalid_xml = self.generateUnsafeXml_bad_string()
self._test_invalid_instance_tree(instance_xml=invalid_xml)
def test_invalid_backspace_char_on_instance_sla_xml(self):
invalid_xml = self.generateUnsafeXml_bad_string()
self._test_invalid_instance_tree(sla_xml=invalid_xml)
...@@ -51,7 +51,9 @@ class TestSlapOSCorePersonRequest(SlapOSTestCaseMixin): ...@@ -51,7 +51,9 @@ class TestSlapOSCorePersonRequest(SlapOSTestCaseMixin):
<instance> <instance>
</instance> </instance>
""" """
sla_xml = "test" sla_xml = """<?xml version="1.0" encoding="utf-8"?>
<instance />
"""
shared = True shared = True
state = "started" state = "started"
...@@ -165,7 +167,9 @@ class TestSlapOSCorePersonRequest(SlapOSTestCaseMixin): ...@@ -165,7 +167,9 @@ class TestSlapOSCorePersonRequest(SlapOSTestCaseMixin):
<instance> <instance>
</instance> </instance>
""" """
sla_xml = "test" sla_xml = """<?xml version="1.0" encoding="utf-8"?>
<instance />
"""
shared = True shared = True
# Only started, stopped, destroyed # Only started, stopped, destroyed
...@@ -229,7 +233,9 @@ class TestSlapOSCorePersonRequest(SlapOSTestCaseMixin): ...@@ -229,7 +233,9 @@ class TestSlapOSCorePersonRequest(SlapOSTestCaseMixin):
<instance> <instance>
</instance> </instance>
""" """
sla_xml = "test" sla_xml = """<?xml version="1.0" encoding="utf-8"?>
<instance />
"""
shared = True shared = True
state = "started" state = "started"
...@@ -257,7 +263,9 @@ class TestSlapOSCorePersonRequest(SlapOSTestCaseMixin): ...@@ -257,7 +263,9 @@ class TestSlapOSCorePersonRequest(SlapOSTestCaseMixin):
<instance> <instance>
</instance> </instance>
""" """
sla_xml = "test" sla_xml = """<?xml version="1.0" encoding="utf-8"?>
<instance />
"""
shared = True shared = True
state = "started" state = "started"
...@@ -298,7 +306,9 @@ class TestSlapOSCorePersonRequest(SlapOSTestCaseMixin): ...@@ -298,7 +306,9 @@ class TestSlapOSCorePersonRequest(SlapOSTestCaseMixin):
<instance> <instance>
</instance> </instance>
""" """
sla_xml = "test" sla_xml = """<?xml version="1.0" encoding="utf-8"?>
<instance />
"""
shared = True shared = True
state = "started" state = "started"
...@@ -335,7 +345,9 @@ class TestSlapOSCorePersonRequest(SlapOSTestCaseMixin): ...@@ -335,7 +345,9 @@ class TestSlapOSCorePersonRequest(SlapOSTestCaseMixin):
<instance> <instance>
</instance> </instance>
""" """
sla_xml = "test" sla_xml = """<?xml version="1.0" encoding="utf-8"?>
<instance />
"""
shared = True shared = True
state = "started" state = "started"
...@@ -404,7 +416,9 @@ class TestSlapOSCorePersonRequest(SlapOSTestCaseMixin): ...@@ -404,7 +416,9 @@ class TestSlapOSCorePersonRequest(SlapOSTestCaseMixin):
<instance> <instance>
</instance> </instance>
""" """
sla_xml = "test" sla_xml = """<?xml version="1.0" encoding="utf-8"?>
<instance />
"""
shared = True shared = True
state = "started" state = "started"
...@@ -518,7 +532,9 @@ class TestSlapOSCorePersonRequest(SlapOSTestCaseMixin): ...@@ -518,7 +532,9 @@ class TestSlapOSCorePersonRequest(SlapOSTestCaseMixin):
<instance> <instance>
</instance> </instance>
""" """
sla_xml = "test" sla_xml = """<?xml version="1.0" encoding="utf-8"?>
<instance />
"""
shared = True shared = True
person.requestSoftwareInstance( person.requestSoftwareInstance(
...@@ -559,7 +575,9 @@ class TestSlapOSCorePersonRequest(SlapOSTestCaseMixin): ...@@ -559,7 +575,9 @@ class TestSlapOSCorePersonRequest(SlapOSTestCaseMixin):
<instance> <instance>
</instance> </instance>
""" """
sla_xml = "test" sla_xml = """<?xml version="1.0" encoding="utf-8"?>
<instance />
"""
shared = True shared = True
person.requestSoftwareInstance( person.requestSoftwareInstance(
......
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