# -*- coding: utf-8 -*-
# Copyright (c) 2002-2012 Nexedi SA and Contributors. All Rights Reserved.
from erp5.component.test.SlapOSTestCaseMixin import SlapOSTestCaseMixin

from DateTime import DateTime
from App.Common import rfc1123_date

import os
import tempfile
import time

# blurb to make nice XML comparisions
import xml.dom.ext.reader.Sax
import xml.dom.ext
import StringIO
import difflib
import hashlib
from binascii import hexlify
from OFS.Traversable import NotFound


def hashData(data):
  return hexlify(hashlib.sha1(data).digest())


class Simulator:
  def __init__(self, outfile, method):
    self.outfile = outfile
    self.method = method

  def __call__(self, *args, **kwargs):
    """Simulation Method"""
    old = open(self.outfile, 'r').read()
    if old:
      l = eval(old) #pylint: disable=eval-used
    else:
      l = []
    l.append({'recmethod': self.method,
      'recargs': args,
      'reckwargs': kwargs})
    open(self.outfile, 'w').write(repr(l))

class TestSlapOSSlapToolMixin(SlapOSTestCaseMixin):
  def afterSetUp(self):
    SlapOSTestCaseMixin.afterSetUp(self)
    self.portal_slap = self.portal.portal_slap

    # Prepare compute_node
    self.compute_node = self.portal.compute_node_module.template_compute_node\
        .Base_createCloneDocument(batch_mode=1)
    self.compute_node.edit(
      title="Compute Node %s" % self.new_id,
      reference="TESTCOMP-%s" % self.new_id
    )
    if getattr(self, "person", None) is not None:
      self.compute_node.edit(
        source_administration_value=getattr(self, "person", None),
        )
    self.compute_node.validate()

    self._addERP5Login(self.compute_node)

    self.tic()

    self.compute_node_id = self.compute_node.getReference()
    self.compute_node_user_id = self.compute_node.getUserId()
    self.pinDateTime(DateTime())

  def beforeTearDown(self):
    self.unpinDateTime()
    self._cleaupREQUEST()


class TestSlapOSSlapToolgetFullComputeNodeInformation(TestSlapOSSlapToolMixin):
  def test_activate_getFullComputeNodeInformation_first_access(self):
    self._makeComplexComputeNode(with_slave=True)
    self.portal.REQUEST['disable_isTestRun'] = True
    self.tic()

    self.login(self.compute_node_user_id)

    # First access.
    # Cache has been filled by interaction workflow
    # (luckily, it seems the cache is filled after everything is indexed)
    response = self.portal_slap.getFullComputeNodeInformation(self.compute_node_id)
    self.commit()
    first_etag = self.portal_slap._calculateRefreshEtag()
    first_body_fingerprint = hashData(
      self.portal_slap._getCacheComputeNodeInformation(self.compute_node_id,
                                                    self.compute_node_id)
    )
    self.assertEqual(200, response.status)
    self.assertTrue('last-modified' not in response.headers)
    self.assertEqual(first_etag, response.headers.get('etag'))
    self.assertEqual(first_body_fingerprint, hashData(response.body))
    self.assertEqual(0, len(self.portal.portal_activities.getMessageList()))

    # Trigger the compute_node reindexation
    # This should trigger a new etag, but the body should be the same
    self.compute_node.reindexObject()
    self.commit()

    # Second access
    # Check that the result is stable, as the indexation timestamp is not changed yet
    current_activity_count = len(self.portal.portal_activities.getMessageList())
    response = self.portal_slap.getFullComputeNodeInformation(self.compute_node_id)
    self.commit()
    self.assertEqual(200, response.status)
    self.assertTrue('last-modified' not in response.headers)
    self.assertEqual(first_etag, response.headers.get('etag'))
    self.assertEqual(first_body_fingerprint, hashData(response.body))
    self.assertEqual(current_activity_count, len(self.portal.portal_activities.getMessageList()))

    self.tic()

    # Third access, new calculation expected
    # The retrieved informations comes from the cache
    # But a new cache modification activity is triggered
    response = self.portal_slap.getFullComputeNodeInformation(self.compute_node_id)
    self.commit()
    self.assertEqual(200, response.status)
    self.assertTrue('last-modified' not in response.headers)
    second_etag = self.portal_slap._calculateRefreshEtag()
    second_body_fingerprint = hashData(
      self.portal_slap._getCacheComputeNodeInformation(self.compute_node_id,
                                                    self.compute_node_id)
    )
    self.assertNotEqual(first_etag, second_etag)
    # The indexation timestamp does not impact the response body
    self.assertEqual(first_body_fingerprint, second_body_fingerprint)
    self.assertEqual(first_etag, response.headers.get('etag'))
    self.assertEqual(first_body_fingerprint, hashData(response.body))
    self.assertEqual(1, len(self.portal.portal_activities.getMessageList()))

    # Execute the cache modification activity
    self.tic()

    # 4th access
    # The new etag value is now used
    response = self.portal_slap.getFullComputeNodeInformation(self.compute_node_id)
    self.commit()
    self.assertEqual(200, response.status)
    self.assertTrue('last-modified' not in response.headers)
    self.assertEqual(second_etag, response.headers.get('etag'))
    self.assertEqual(first_body_fingerprint, hashData(response.body))
    self.assertEqual(0, len(self.portal.portal_activities.getMessageList()))

    # Edit the instance
    # This should trigger a new etag and a new body
    self.stop_requested_software_instance.edit(text_content=self.generateSafeXml())
    self.commit()

    # 5th access
    # Check that the result is stable, as the indexation timestamp is not changed yet
    current_activity_count = len(self.portal.portal_activities.getMessageList())
    # Edition does not impact the etag
    self.assertEqual(second_etag, self.portal_slap._calculateRefreshEtag())
    third_body_fingerprint = hashData(
      self.portal_slap._getCacheComputeNodeInformation(self.compute_node_id,
                                                    self.compute_node_id)
    )
    # The edition impacts the response body
    self.assertNotEqual(first_body_fingerprint, third_body_fingerprint)
    response = self.portal_slap.getFullComputeNodeInformation(self.compute_node_id)
    self.commit()
    self.assertEqual(200, response.status)
    self.assertTrue('last-modified' not in response.headers)
    self.assertEqual(second_etag, response.headers.get('etag'))
    self.assertEqual(first_body_fingerprint, hashData(response.body))
    self.assertEqual(current_activity_count, len(self.portal.portal_activities.getMessageList()))

    self.tic()

    # 6th, the instance edition triggered an interaction workflow
    # which updated the cache
    response = self.portal_slap.getFullComputeNodeInformation(self.compute_node_id)
    self.commit()
    self.assertEqual(200, response.status)
    self.assertTrue('last-modified' not in response.headers)
    third_etag = self.portal_slap._calculateRefreshEtag()
    self.assertNotEqual(second_etag, third_etag)
    self.assertEqual(third_etag, response.headers.get('etag'))
    self.assertEqual(third_body_fingerprint, hashData(response.body))
    self.assertEqual(0, len(self.portal.portal_activities.getMessageList()))

    # Remove the slave link to the partition
    # Compute Node should loose permission to access the slave instance
    self.start_requested_slave_instance.setAggregate('')
    self.commit()

    # 7th access
    # Check that the result is stable, as the indexation timestamp is not changed yet
    current_activity_count = len(self.portal.portal_activities.getMessageList())
    # Edition does not impact the etag
    self.assertEqual(third_etag, self.portal_slap._calculateRefreshEtag())
    # The edition does not impact the response body yet, as the aggregate relation
    # is not yet unindex
    self.assertEqual(third_body_fingerprint, hashData(
      self.portal_slap._getCacheComputeNodeInformation(self.compute_node_id,
                                                    self.compute_node_id)
    ))
    response = self.portal_slap.getFullComputeNodeInformation(self.compute_node_id)
    self.commit()
    self.assertEqual(200, response.status)
    self.assertTrue('last-modified' not in response.headers)
    self.assertEqual(third_etag, response.headers.get('etag'))
    self.assertEqual(third_body_fingerprint, hashData(response.body))
    self.assertEqual(current_activity_count, len(self.portal.portal_activities.getMessageList()))

    self.tic()

    # 8th access
    # changing the aggregate relation trigger the partition reindexation
    # which trigger cache modification activity
    # So, we should get the correct cached value
    response = self.portal_slap.getFullComputeNodeInformation(self.compute_node_id)
    self.commit()
    self.assertEqual(200, response.status)
    self.assertTrue('last-modified' not in response.headers)
    fourth_etag = self.portal_slap._calculateRefreshEtag()
    fourth_body_fingerprint = hashData(
      self.portal_slap._getCacheComputeNodeInformation(self.compute_node_id,
                                                    self.compute_node_id)
    )
    self.assertNotEqual(third_etag, fourth_etag)
    # The indexation timestamp does not impact the response body
    self.assertNotEqual(third_body_fingerprint, fourth_body_fingerprint)
    self.assertEqual(fourth_etag, response.headers.get('etag'))
    self.assertEqual(fourth_body_fingerprint, hashData(response.body))
    self.assertEqual(0, len(self.portal.portal_activities.getMessageList()))


class TestSlapOSSlapToolComputeNodeAccess(TestSlapOSSlapToolMixin):
  def test_getFullComputeNodeInformation(self):
    self._makeComplexComputeNode(with_slave=True)

    partition_1_root_instance_title = self.compute_node.partition1.getAggregateRelatedValue(
      portal_type='Software Instance').getSpecialiseValue().getTitle()
    partition_2_root_instance_title = self.compute_node.partition2.getAggregateRelatedValue(
      portal_type='Software Instance').getSpecialiseValue().getTitle()
    partition_3_root_instance_title = self.compute_node.partition3.getAggregateRelatedValue(
      portal_type='Software Instance').getSpecialiseValue().getTitle()

    self.login(self.compute_node_user_id)
    response = self.portal_slap.getFullComputeNodeInformation(self.compute_node_id)
    self.assertEqual(200, response.status)
    self.assertEqual('public, max-age=1, stale-if-error=604800',
        response.headers.get('cache-control'))
    self.assertEqual('REMOTE_USER',
        response.headers.get('vary'))
    self.assertFalse('etag' in response.headers)
    self.assertEqual('text/xml; charset=utf-8',
        response.headers.get('content-type'))

    # check returned XML
    xml_fp = StringIO.StringIO()

    xml.dom.ext.PrettyPrint(xml.dom.ext.reader.Sax.FromXml(response.body),
        stream=xml_fp)
    xml_fp.seek(0)
    got_xml = xml_fp.read()
    expected_xml = """<?xml version='1.0' encoding='UTF-8'?>
<marshal>
  <object id='i2' module='slapos.slap.slap' class='Compute Node'>
    <tuple>
      <string>%(compute_node_id)s</string>
    </tuple>
    <dictionary id='i3'>
      <string>_compute_node_id</string>
      <string>%(compute_node_id)s</string>
      <string>_compute_partition_list</string>
      <list id='i4'>
        <object id='i5' module='slapos.slap.slap' class='ComputePartition'>
          <tuple>
            <string>%(compute_node_id)s</string>
            <string>partition4</string>
          </tuple>
          <dictionary id='i6'>
            <string>_compute_node_id</string>
            <string>%(compute_node_id)s</string>
            <string>_need_modification</string>
            <int>0</int>
            <string>_partition_id</string>
            <string>partition4</string>
            <string>_request_dict</string>
            <none/>
            <string>_requested_state</string>
            <string>destroyed</string>
            <string>_software_release_document</string>
            <none/>
          </dictionary>
        </object>
        <object id='i7' module='slapos.slap.slap' class='ComputePartition'>
          <tuple>
            <string>%(compute_node_id)s</string>
            <string>partition3</string>
          </tuple>
          <dictionary id='i8'>
            <string>_access_status</string>
            <string>%(access_status)s</string>
            <string>_compute_node_id</string>
            <string>%(compute_node_id)s</string>
            <string>_connection_dict</string>
            <dictionary id='i9'/>
            <string>_filter_dict</string>
            <dictionary id='i10'>
              <string>paramé</string>
              <string>%(partition_3_sla)s</string>
            </dictionary>
            <string>_instance_guid</string>
            <string>%(partition_3_instance_guid)s</string>
            <string>_need_modification</string>
            <int>1</int>
            <string>_parameter_dict</string>
            <dictionary id='i11'>
              <string>full_ip_list</string>
              <list id='i12'/>
              <string>instance_title</string>
              <string>%(partition_3_instance_title)s</string>
              <string>ip_list</string>
              <list id='i13'>
                <tuple>
                  <string/>
                  <string>ip_address_3</string>
                </tuple>
              </list>
              <string>paramé</string>
              <string>%(partition_3_param)s</string>
              <string>root_instance_short_title</string>
              <string/>
              <string>root_instance_title</string>
              <string>%(partition_3_root_instance_title)s</string>
              <string>slap_compute_node_id</string>
              <string>%(compute_node_id)s</string>
              <string>slap_compute_partition_id</string>
              <string>partition3</string>
              <string>slap_software_release_url</string>
              <string>%(partition_3_software_release_url)s</string>
              <string>slap_software_type</string>
              <string>%(partition_3_instance_software_type)s</string>
              <string>slave_instance_list</string>
              <list id='i14'/>
              <string>timestamp</string>
              <string>%(partition_3_timestamp)s</string>
            </dictionary>
            <string>_partition_id</string>
            <string>partition3</string>
            <string>_request_dict</string>
            <none/>
            <string>_requested_state</string>
            <string>destroyed</string>
            <string>_software_release_document</string>
            <object id='i15' module='slapos.slap.slap' class='SoftwareRelease'>
              <tuple>
                <string>%(partition_3_software_release_url)s</string>
                <string>%(compute_node_id)s</string>
              </tuple>
              <dictionary id='i16'>
                <string>_compute_node_guid</string>
                <string>%(compute_node_id)s</string>
                <string>_software_instance_list</string>
                <list id='i17'/>
                <string>_software_release</string>
                <string>%(partition_3_software_release_url)s</string>
              </dictionary>
            </object>
          </dictionary>
        </object>
        <object id='i18' module='slapos.slap.slap' class='ComputePartition'>
          <tuple>
            <string>%(compute_node_id)s</string>
            <string>partition2</string>
          </tuple>
          <dictionary id='i19'>
            <string>_access_status</string>
            <string>%(access_status)s</string>
            <string>_compute_node_id</string>
            <string>%(compute_node_id)s</string>
            <string>_connection_dict</string>
            <dictionary id='i20'/>
            <string>_filter_dict</string>
            <dictionary id='i21'>
              <string>paramé</string>
              <string>%(partition_2_sla)s</string>
            </dictionary>
            <string>_instance_guid</string>
            <string>%(partition_2_instance_guid)s</string>
            <string>_need_modification</string>
            <int>1</int>
            <string>_parameter_dict</string>
            <dictionary id='i22'>
              <string>full_ip_list</string>
              <list id='i23'/>
              <string>instance_title</string>
              <string>%(partition_2_instance_title)s</string>
              <string>ip_list</string>
              <list id='i24'>
                <tuple>
                  <string/>
                  <string>ip_address_2</string>
                </tuple>
              </list>
              <string>paramé</string>
              <string>%(partition_2_param)s</string>
              <string>root_instance_short_title</string>
              <string/>
              <string>root_instance_title</string>
              <string>%(partition_2_root_instance_title)s</string>
              <string>slap_compute_node_id</string>
              <string>%(compute_node_id)s</string>
              <string>slap_compute_partition_id</string>
              <string>partition2</string>
              <string>slap_software_release_url</string>
              <string>%(partition_2_software_release_url)s</string>
              <string>slap_software_type</string>
              <string>%(partition_2_instance_software_type)s</string>
              <string>slave_instance_list</string>
              <list id='i25'/>
              <string>timestamp</string>
              <string>%(partition_2_timestamp)s</string>
            </dictionary>
            <string>_partition_id</string>
            <string>partition2</string>
            <string>_request_dict</string>
            <none/>
            <string>_requested_state</string>
            <string>stopped</string>
            <string>_software_release_document</string>
            <object id='i26' module='slapos.slap.slap' class='SoftwareRelease'>
              <tuple>
                <string>%(partition_2_software_release_url)s</string>
                <string>%(compute_node_id)s</string>
              </tuple>
              <dictionary id='i27'>
                <string>_compute_node_guid</string>
                <string>%(compute_node_id)s</string>
                <string>_software_instance_list</string>
                <list id='i28'/>
                <string>_software_release</string>
                <string>%(partition_2_software_release_url)s</string>
              </dictionary>
            </object>
          </dictionary>
        </object>
        <object id='i29' module='slapos.slap.slap' class='ComputePartition'>
          <tuple>
            <string>%(compute_node_id)s</string>
            <string>partition1</string>
          </tuple>
          <dictionary id='i30'>
            <string>_access_status</string>
            <string>%(access_status)s</string>
            <string>_compute_node_id</string>
            <string>%(compute_node_id)s</string>
            <string>_connection_dict</string>
            <dictionary id='i31'/>
            <string>_filter_dict</string>
            <dictionary id='i32'>
              <string>paramé</string>
              <string>%(partition_1_sla)s</string>
            </dictionary>
            <string>_instance_guid</string>
            <string>%(partition_1_instance_guid)s</string>
            <string>_need_modification</string>
            <int>1</int>
            <string>_parameter_dict</string>
            <dictionary id='i33'>
              <string>full_ip_list</string>
              <list id='i34'/>
              <string>instance_title</string>
              <string>%(partition_1_instance_title)s</string>
              <string>ip_list</string>
              <list id='i35'>
                <tuple>
                  <string/>
                  <string>ip_address_1</string>
                </tuple>
              </list>
              <string>paramé</string>
              <string>%(partition_1_param)s</string>
              <string>root_instance_short_title</string>
              <string/>
              <string>root_instance_title</string>
              <string>%(partition_1_root_instance_title)s</string>
              <string>slap_compute_node_id</string>
              <string>%(compute_node_id)s</string>
              <string>slap_compute_partition_id</string>
              <string>partition1</string>
              <string>slap_software_release_url</string>
              <string>%(partition_1_software_release_url)s</string>
              <string>slap_software_type</string>
              <string>%(partition_1_instance_software_type)s</string>
              <string>slave_instance_list</string>
              <list id='i36'>
                <dictionary id='i37'>
                  <string>paramé</string>
                  <string>%(slave_1_param)s</string>
                  <string>slap_software_type</string>
                  <string>%(slave_1_software_type)s</string>
                  <string>slave_reference</string>
                  <string>%(slave_1_instance_guid)s</string>
                  <string>slave_title</string>
                  <string>%(slave_1_title)s</string>
                  <string>timestamp</string>
                  <int>%(partition_1_timestamp)s</int>
                </dictionary>
              </list>
              <string>timestamp</string>
              <string>%(partition_1_timestamp)s</string>
            </dictionary>
            <string>_partition_id</string>
            <string>partition1</string>
            <string>_request_dict</string>
            <none/>
            <string>_requested_state</string>
            <string>started</string>
            <string>_software_release_document</string>
            <object id='i38' module='slapos.slap.slap' class='SoftwareRelease'>
              <tuple>
                <string>%(partition_1_software_release_url)s</string>
                <string>%(compute_node_id)s</string>
              </tuple>
              <dictionary id='i39'>
                <string>_compute_node_guid</string>
                <string>%(compute_node_id)s</string>
                <string>_software_instance_list</string>
                <list id='i40'/>
                <string>_software_release</string>
                <string>%(partition_1_software_release_url)s</string>
              </dictionary>
            </object>
          </dictionary>
        </object>
      </list>
      <string>_software_release_list</string>
      <list id='i41'>
        <object id='i42' module='slapos.slap.slap' class='SoftwareRelease'>
          <tuple>
            <string>%(destroy_requested_url)s</string>
            <string>%(compute_node_id)s</string>
          </tuple>
          <dictionary id='i43'>
            <string>_compute_node_guid</string>
            <string>%(compute_node_id)s</string>
            <string>_known_state</string>
            <string>error</string>
            <string>_requested_state</string>
            <string>destroyed</string>
            <string>_software_instance_list</string>
            <list id='i44'/>
            <string>_software_release</string>
            <string>%(destroy_requested_url)s</string>
          </dictionary>
        </object>
        <object id='i45' module='slapos.slap.slap' class='SoftwareRelease'>
          <tuple>
            <string>%(start_requested_url)s</string>
            <string>%(compute_node_id)s</string>
          </tuple>
          <dictionary id='i46'>
            <string>_compute_node_guid</string>
            <string>%(compute_node_id)s</string>
            <string>_known_state</string>
            <string>error</string>
            <string>_requested_state</string>
            <string>available</string>
            <string>_software_instance_list</string>
            <list id='i47'/>
            <string>_software_release</string>
            <string>%(start_requested_url)s</string>
          </dictionary>
        </object>
      </list>
    </dictionary>
  </object>
</marshal>
""" % dict(
  compute_node_id=self.compute_node_id,
  destroy_requested_url=self.destroy_requested_software_installation.getUrlString(),
  partition_1_instance_guid=self.compute_node.partition1.getAggregateRelatedValue(portal_type='Software Instance').getReference(),
  partition_1_instance_title=self.compute_node.partition1.getAggregateRelatedValue(portal_type='Software Instance').getTitle(),
  partition_1_root_instance_title=partition_1_root_instance_title,
  partition_1_instance_software_type=self.compute_node.partition1.getAggregateRelatedValue(portal_type='Software Instance').getSourceReference(),
  partition_1_param=self.compute_node.partition1.getAggregateRelatedValue(portal_type='Software Instance').getInstanceXmlAsDict()['paramé'],
  partition_1_sla=self.compute_node.partition1.getAggregateRelatedValue(portal_type='Software Instance').getSlaXmlAsDict()['paramé'],
  partition_1_software_release_url=self.compute_node.partition1.getAggregateRelatedValue(portal_type='Software Instance').getUrlString(),
  partition_1_timestamp=int(self.compute_node.partition1.getAggregateRelatedValue(portal_type='Software Instance').getModificationDate()),
  partition_2_instance_guid=self.compute_node.partition2.getAggregateRelatedValue(portal_type='Software Instance').getReference(),
  partition_2_instance_title=self.compute_node.partition2.getAggregateRelatedValue(portal_type='Software Instance').getTitle(),
  partition_2_root_instance_title=partition_2_root_instance_title,
  partition_2_instance_software_type=self.compute_node.partition2.getAggregateRelatedValue(portal_type='Software Instance').getSourceReference(),
  partition_2_param=self.compute_node.partition2.getAggregateRelatedValue(portal_type='Software Instance').getInstanceXmlAsDict()['paramé'],
  partition_2_sla=self.compute_node.partition2.getAggregateRelatedValue(portal_type='Software Instance').getSlaXmlAsDict()['paramé'],
  partition_2_software_release_url=self.compute_node.partition2.getAggregateRelatedValue(portal_type='Software Instance').getUrlString(),
  partition_2_timestamp=int(self.compute_node.partition2.getAggregateRelatedValue(portal_type='Software Instance').getModificationDate()),
  partition_3_instance_guid=self.compute_node.partition3.getAggregateRelatedValue(portal_type='Software Instance').getReference(),
  partition_3_instance_title=self.compute_node.partition3.getAggregateRelatedValue(portal_type='Software Instance').getTitle(),
  partition_3_root_instance_title=partition_3_root_instance_title,
  partition_3_instance_software_type=self.compute_node.partition3.getAggregateRelatedValue(portal_type='Software Instance').getSourceReference(),
  partition_3_param=self.compute_node.partition3.getAggregateRelatedValue(portal_type='Software Instance').getInstanceXmlAsDict()['paramé'],
  partition_3_sla=self.compute_node.partition3.getAggregateRelatedValue(portal_type='Software Instance').getSlaXmlAsDict()['paramé'],
  partition_3_software_release_url=self.compute_node.partition3.getAggregateRelatedValue(portal_type='Software Instance').getUrlString(),
  partition_3_timestamp=int(self.compute_node.partition3.getAggregateRelatedValue(portal_type='Software Instance').getModificationDate()),
  slave_1_param=self.start_requested_slave_instance.getInstanceXmlAsDict()['paramé'],
  slave_1_software_type=self.start_requested_slave_instance.getSourceReference(),
  slave_1_instance_guid=self.start_requested_slave_instance.getReference(),
  slave_1_title=self.start_requested_slave_instance.getTitle(),
  start_requested_url=self.start_requested_software_installation.getUrlString(),
  access_status="#error no data found!",
)
    self.assertEqual(expected_xml, got_xml,
        '\n'.join([q for q in difflib.unified_diff(expected_xml.split('\n'), got_xml.split('\n'))]))

  def test_getComputeNodeInformation(self):
    self.assertEqual('getFullComputeNodeInformation',
      self.portal_slap.getComputeNodeInformation.im_func.func_name)

  def test_not_accessed_getComputeNodeStatus(self):
    self.login(self.compute_node_user_id)
    created_at = rfc1123_date(DateTime())
    since = created_at
    response = self.portal_slap.getComputeNodeStatus(self.compute_node_id)
    self.assertEqual(200, response.status)
    self.assertEqual('public, max-age=60, stale-if-error=604800',
        response.headers.get('cache-control'))
    self.assertEqual('REMOTE_USER',
        response.headers.get('vary'))
    self.assertTrue('last-modified' in response.headers)
    self.assertEqual('text/xml; charset=utf-8',
        response.headers.get('content-type'))
    # check returned XML
    xml_fp = StringIO.StringIO()

    xml.dom.ext.PrettyPrint(xml.dom.ext.reader.Sax.FromXml(response.body),
        stream=xml_fp)
    xml_fp.seek(0)
    got_xml = xml_fp.read()

    expected_xml = """\
<?xml version='1.0' encoding='UTF-8'?>
<marshal>
  <dictionary id='i2'>
    <string>created_at</string>
    <string>%(created_at)s</string>
    <string>since</string>
    <string>%(since)s</string>
    <string>state</string>
    <string/>
    <string>text</string>
    <string>#error no data found for %(compute_node_id)s</string>
    <string>user</string>
    <string>SlapOS Master</string>
  </dictionary>
</marshal>
""" % dict(
  created_at=created_at,
  since=since,
  compute_node_id=self.compute_node_id
)
    self.assertEqual(expected_xml, got_xml,
        '\n'.join([q for q in difflib.unified_diff(expected_xml.split('\n'), got_xml.split('\n'))]))

  def test_accessed_getComputeNodeStatus(self):
    self.login(self.compute_node_user_id)

    self.portal_slap.getFullComputeNodeInformation(self.compute_node_id)
    created_at = rfc1123_date(DateTime())
    since = created_at
    response = self.portal_slap.getComputeNodeStatus(self.compute_node_id)
    self.assertEqual(200, response.status)
    self.assertEqual('public, max-age=60, stale-if-error=604800',
        response.headers.get('cache-control'))
    self.assertEqual('REMOTE_USER',
        response.headers.get('vary'))
    self.assertTrue('last-modified' in response.headers)
    self.assertEqual('text/xml; charset=utf-8',
        response.headers.get('content-type'))

    # check returned XML
    xml_fp = StringIO.StringIO()

    xml.dom.ext.PrettyPrint(xml.dom.ext.reader.Sax.FromXml(response.body),
        stream=xml_fp)
    xml_fp.seek(0)
    got_xml = xml_fp.read()

    expected_xml = """\
<?xml version='1.0' encoding='UTF-8'?>
<marshal>
  <dictionary id='i2'>
    <string>created_at</string>
    <string>%(created_at)s</string>
    <string>since</string>
    <string>%(since)s</string>
    <string>state</string>
    <string/>
    <string>text</string>
    <string>#access %(compute_node_id)s</string>
    <string>user</string>
    <string>%(compute_node_id)s</string>
  </dictionary>
</marshal>
""" % dict(
  created_at=created_at,
  since=since,
  compute_node_id=self.compute_node_id
)
    self.assertEqual(expected_xml, got_xml,
        '\n'.join([q for q in difflib.unified_diff(expected_xml.split('\n'), got_xml.split('\n'))]))

  def assertComputeNodeBangSimulator(self, args, kwargs):
    stored = eval(open(self.compute_node_bang_simulator).read()) #pylint: disable=eval-used
    # do the same translation magic as in workflow
    kwargs['comment'] = kwargs.pop('comment')
    self.assertEqual(stored,
      [{'recargs': args, 'reckwargs': kwargs,
      'recmethod': 'reportComputeNodeBang'}])

  def test_compute_nodeBang(self):
    self._makeComplexComputeNode()
    self.compute_node_bang_simulator = tempfile.mkstemp()[1]
    try:
      self.login(self.compute_node_user_id)
      self.compute_node.reportComputeNodeBang = Simulator(
        self.compute_node_bang_simulator, 'reportComputeNodeBang')
      error_log = 'Please bang me'
      response = self.portal_slap.compute_nodeBang(self.compute_node_id,
        error_log)
      self.assertEqual('None', response)
      created_at = rfc1123_date(DateTime())
      since = created_at
      response = self.portal_slap.getComputeNodeStatus(self.compute_node_id)
      # check returned XML
      xml_fp = StringIO.StringIO()

      xml.dom.ext.PrettyPrint(xml.dom.ext.reader.Sax.FromXml(response.body),
          stream=xml_fp)
      xml_fp.seek(0)
      got_xml = xml_fp.read()
      expected_xml = """\
<?xml version='1.0' encoding='UTF-8'?>
<marshal>
  <dictionary id='i2'>
    <string>created_at</string>
    <string>%(created_at)s</string>
    <string>since</string>
    <string>%(since)s</string>
    <string>state</string>
    <string/>
    <string>text</string>
    <string>#error bang</string>
    <string>user</string>
    <string>%(compute_node_id)s</string>
  </dictionary>
</marshal>
""" % dict(
    created_at=created_at,
    since=since,
    compute_node_id=self.compute_node_id,
  )
      self.assertEqual(expected_xml, got_xml,
          '\n'.join([q for q in difflib.unified_diff(expected_xml.split('\n'), got_xml.split('\n'))]))
      self.assertComputeNodeBangSimulator((), {'comment': error_log})
    finally:
      if os.path.exists(self.compute_node_bang_simulator):
        os.unlink(self.compute_node_bang_simulator)

  def assertLoadComputeNodeConfigurationFromXML(self, args, kwargs):
    stored = eval(open(self.compute_node_load_configuration_simulator).read()) #pylint: disable=eval-used
    # do the same translation magic as in workflow
    self.assertEqual(stored,
      [{'recargs': args, 'reckwargs': kwargs,
      'recmethod': 'ComputeNode_updateFromDict'}])

  def test_loadComputeNodeConfigurationFromXML(self):
    self.compute_node_load_configuration_simulator = tempfile.mkstemp()[1]
    try:
      self.login(self.compute_node_user_id)
      self.compute_node.ComputeNode_updateFromDict = Simulator(
        self.compute_node_load_configuration_simulator, 'ComputeNode_updateFromDict')

      compute_node_xml = """\
<?xml version='1.0' encoding='UTF-8'?>
<marshal>
  <dictionary id='i2'>
    <string>reference</string>
    <string>%(compute_node_reference)s</string>
  </dictionary>
</marshal>
""" % {'compute_node_reference': self.compute_node.getReference()}

      response = self.portal_slap.loadComputeNodeConfigurationFromXML(
        compute_node_xml)
      self.assertEqual('Content properly posted.', response)
      self.assertLoadComputeNodeConfigurationFromXML(
        ({'reference': self.compute_node.getReference()},), {})
    finally:
      if os.path.exists(self.compute_node_load_configuration_simulator):
        os.unlink(self.compute_node_load_configuration_simulator)
  
  def test_not_accessed_getSoftwareInstallationStatus(self):
    self._makeComplexComputeNode()
    self.compute_node_bang_simulator = tempfile.mkstemp()[1]
    self.login(self.compute_node_user_id)
    created_at = rfc1123_date(DateTime())
    since = created_at
    software_installation = self.start_requested_software_installation
    url_string = software_installation.getUrlString()
    response = self.portal_slap.getSoftwareInstallationStatus(url_string,
                            self.compute_node_id)
    self.assertEqual(200, response.status)
    self.assertEqual('public, max-age=60, stale-if-error=604800',
        response.headers.get('cache-control'))
    self.assertEqual('REMOTE_USER',
        response.headers.get('vary'))
    self.assertTrue('last-modified' in response.headers)
    self.assertEqual('text/xml; charset=utf-8',
        response.headers.get('content-type'))
    # check returned XML
    xml_fp = StringIO.StringIO()

    xml.dom.ext.PrettyPrint(xml.dom.ext.reader.Sax.FromXml(response.body),
        stream=xml_fp)
    xml_fp.seek(0)
    got_xml = xml_fp.read()

    expected_xml = """\
<?xml version='1.0' encoding='UTF-8'?>
<marshal>
  <dictionary id='i2'>
    <string>created_at</string>
    <string>%(created_at)s</string>
    <string>since</string>
    <string>%(since)s</string>
    <string>state</string>
    <string/>
    <string>text</string>
    <string>#error no data found for %(reference)s</string>
    <string>user</string>
    <string>SlapOS Master</string>
  </dictionary>
</marshal>
""" % dict(
  created_at=created_at,
  since=since,
  reference=software_installation.getReference()
)
    self.assertEqual(expected_xml, got_xml,
        '\n'.join([q for q in difflib.unified_diff(expected_xml.split('\n'), got_xml.split('\n'))]))
    

  def test_destroyedSoftwareRelease_noSoftwareInstallation(self):
    self.login(self.compute_node_user_id)
    self.assertRaises(NotFound,
        self.portal_slap.destroyedSoftwareRelease,
        "http://example.org/foo", self.compute_node_id)

  def test_destroyedSoftwareRelease_noDestroyRequested(self):
    self._makeComplexComputeNode()
    self.login(self.compute_node_user_id)
    self.assertRaises(NotFound,
        self.portal_slap.destroyedSoftwareRelease,
        self.start_requested_software_installation.getUrlString(),
        self.compute_node_id)

  def test_destroyedSoftwareRelease_destroyRequested(self):
    self._makeComplexComputeNode()
    self.login(self.compute_node_user_id)
    destroy_requested = self.destroy_requested_software_installation
    self.assertEqual(destroy_requested.getValidationState(), "validated")
    self.portal_slap.destroyedSoftwareRelease(
        destroy_requested.getUrlString(), self.compute_node_id)
    self.assertEqual(destroy_requested.getValidationState(), "invalidated")

  def test_availableSoftwareRelease(self):
    self._makeComplexComputeNode()
    self.compute_node_bang_simulator = tempfile.mkstemp()[1]
    self.login(self.compute_node_user_id)
    software_installation = self.start_requested_software_installation
    url_string = software_installation.getUrlString()
    response = self.portal_slap.availableSoftwareRelease(
                  url_string, self.compute_node_id)
    self.assertEqual('None', response)
    created_at = rfc1123_date(DateTime())
    since = created_at
    response = self.portal_slap.getSoftwareInstallationStatus(
                      url_string, self.compute_node_id)
    # check returned XML
    xml_fp = StringIO.StringIO()

    xml.dom.ext.PrettyPrint(xml.dom.ext.reader.Sax.FromXml(response.body),
        stream=xml_fp)
    xml_fp.seek(0)
    got_xml = xml_fp.read()
    expected_xml = """\
<?xml version='1.0' encoding='UTF-8'?>
<marshal>
  <dictionary id='i2'>
    <string>created_at</string>
    <string>%(created_at)s</string>
    <string>since</string>
    <string>%(since)s</string>
    <string>state</string>
    <string>available</string>
    <string>text</string>
    <string>#access software release %(url_string)s available</string>
    <string>user</string>
    <string>%(compute_node_id)s</string>
  </dictionary>
</marshal>
""" % dict(
    created_at=created_at,
    since=since,
    url_string=url_string,
    compute_node_id=self.compute_node_id,
  )
    self.assertEqual(expected_xml, got_xml,
        '\n'.join([q for q in difflib.unified_diff(expected_xml.split('\n'), got_xml.split('\n'))]))

  def test_buildingSoftwareRelease(self):
    self._makeComplexComputeNode()
    self.compute_node_bang_simulator = tempfile.mkstemp()[1]
    self.login(self.compute_node_user_id)
    software_installation = self.start_requested_software_installation
    url_string = software_installation.getUrlString()
    response = self.portal_slap.buildingSoftwareRelease(
                  url_string, self.compute_node_id)
    self.assertEqual('None', response)
    created_at = rfc1123_date(DateTime())
    since = created_at
    response = self.portal_slap.getSoftwareInstallationStatus(
                      url_string, self.compute_node_id)
    # check returned XML
    xml_fp = StringIO.StringIO()

    xml.dom.ext.PrettyPrint(xml.dom.ext.reader.Sax.FromXml(response.body),
        stream=xml_fp)
    xml_fp.seek(0)
    got_xml = xml_fp.read()
    expected_xml = """\
<?xml version='1.0' encoding='UTF-8'?>
<marshal>
  <dictionary id='i2'>
    <string>created_at</string>
    <string>%(created_at)s</string>
    <string>since</string>
    <string>%(since)s</string>
    <string>state</string>
    <string>building</string>
    <string>text</string>
    <string>#building software release %(url_string)s</string>
    <string>user</string>
    <string>%(compute_node_id)s</string>
  </dictionary>
</marshal>
""" % dict(
    created_at=created_at,
    since=since,
    url_string=url_string,
    compute_node_id=self.compute_node_id,
  )
    self.assertEqual(expected_xml, got_xml,
        '\n'.join([q for q in difflib.unified_diff(expected_xml.split('\n'), got_xml.split('\n'))]))

  def test_softwareReleaseError(self):
    self._makeComplexComputeNode()
    self.compute_node_bang_simulator = tempfile.mkstemp()[1]
    self.login(self.compute_node_user_id)
    software_installation = self.start_requested_software_installation
    url_string = software_installation.getUrlString()
    response = self.portal_slap.softwareReleaseError(
                  url_string, self.compute_node_id, 'error log')
    self.assertEqual('None', response)
    created_at = rfc1123_date(DateTime())
    since = created_at
    response = self.portal_slap.getSoftwareInstallationStatus(
                      url_string, self.compute_node_id)
    # check returned XML
    xml_fp = StringIO.StringIO()

    xml.dom.ext.PrettyPrint(xml.dom.ext.reader.Sax.FromXml(response.body),
        stream=xml_fp)
    xml_fp.seek(0)
    got_xml = xml_fp.read()
    expected_xml = """\
<?xml version='1.0' encoding='UTF-8'?>
<marshal>
  <dictionary id='i2'>
    <string>created_at</string>
    <string>%(created_at)s</string>
    <string>since</string>
    <string>%(since)s</string>
    <string>state</string>
    <string/>
    <string>text</string>
    <string>#error while installing %(url_string)s</string>
    <string>user</string>
    <string>%(compute_node_id)s</string>
  </dictionary>
</marshal>
""" % dict(
    created_at=created_at,
    since=since,
    url_string=url_string,
    compute_node_id=self.compute_node_id,
  )
    self.assertEqual(expected_xml, got_xml,
        '\n'.join([q for q in difflib.unified_diff(expected_xml.split('\n'), got_xml.split('\n'))]))

  def test_useComputeNode_wrong_xml(self):
    self.login(self.compute_node_user_id)
    response = self.portal_slap.useComputeNode(
        self.compute_node_id, "foobar")
    self.assertEqual(400, response.status)
    self.assertEqual("", response.body)

  def assertReportComputeNodeConsumption(self, args, kwargs):
    stored = eval(open(self.compute_node_use_compute_node_simulator).read()) #pylint: disable=eval-used
    # do the same translation magic as in workflow
    self.assertEqual(stored,
      [{'recargs': args, 'reckwargs': kwargs,
      'recmethod': 'ComputeNode_reportComputeNodeConsumption'}])

  def test_useComputeNode_expected_xml(self):
    self.compute_node_use_compute_node_simulator = tempfile.mkstemp()[1]
    try:
      self.login(self.compute_node_user_id)
      self.compute_node.ComputeNode_reportComputeNodeConsumption = Simulator(
        self.compute_node_use_compute_node_simulator,
        'ComputeNode_reportComputeNodeConsumption')
  
      consumption_xml = """<?xml version='1.0' encoding='utf-8'?>
<journal>
<transaction type="Sale Packing List">
<title>Resource consumptions</title>
<start_date></start_date>
<stop_date></stop_date>
<reference>testusagé</reference>
<currency></currency>
<payment_mode></payment_mode>
<category></category>
<arrow type="Administration">
<source></source>
<destination></destination>
</arrow>
<movement>
<resource>CPU Consumption</resource>
<title>Title Sale Packing List Line 1</title>
<reference>slappart0</reference>
<quantity>42.42</quantity>
<price>0.00</price>
<VAT>None</VAT>
<category>None</category>
</movement>
</transaction>
</journal>"""
  
      response = self.portal_slap.useComputeNode(
        self.compute_node_id, consumption_xml)
      self.assertEqual(200, response.status)
      self.assertEqual("OK", response.body)
      self.assertReportComputeNodeConsumption(
        ("testusagé", consumption_xml,), {})
    finally:
      if os.path.exists(self.compute_node_use_compute_node_simulator):
        os.unlink(self.compute_node_use_compute_node_simulator)

  def test_useComputeNode_empty_reference(self):
    self.compute_node_use_compute_node_simulator = tempfile.mkstemp()[1]
    try:
      self.login(self.compute_node_user_id)
      self.compute_node.ComputeNode_reportComputeNodeConsumption = Simulator(
        self.compute_node_use_compute_node_simulator,
        'ComputeNode_reportComputeNodeConsumption')
  
      consumption_xml = """<?xml version='1.0' encoding='utf-8'?>
<journal>
<transaction type="Sale Packing List">
<title>Resource consumptions</title>
<start_date></start_date>
<stop_date></stop_date>
<reference></reference>
<currency></currency>
<payment_mode></payment_mode>
<category></category>
<arrow type="Administration">
<source></source>
<destination></destination>
</arrow>
<movement>
<resource>CPU Consumption</resource>
<title>Title Sale Packing List Line 1</title>
<reference>slappart0</reference>
<quantity>42.42</quantity>
<price>0.00</price>
<VAT>None</VAT>
<category>None</category>
</movement>
</transaction>
</journal>"""
  
      response = self.portal_slap.useComputeNode(
        self.compute_node_id, consumption_xml)
      self.assertEqual(200, response.status)
      self.assertEqual("OK", response.body)
      self.assertReportComputeNodeConsumption(
        ("", consumption_xml,), {})
    finally:
      if os.path.exists(self.compute_node_use_compute_node_simulator):
        os.unlink(self.compute_node_use_compute_node_simulator)


class TestSlapOSSlapToolInstanceAccess(TestSlapOSSlapToolMixin):
  def test_getComputePartitionCertificate(self):
    self._makeComplexComputeNode()
    partition_id = self.start_requested_software_instance.getAggregateValue(
        portal_type='Compute Partition').getReference()
    self.login(self.start_requested_software_instance.getUserId())
    response = self.portal_slap.getComputePartitionCertificate(self.compute_node_id,
        partition_id)
    self.assertEqual(200, response.status)
    self.assertEqual( 'public, max-age=0, must-revalidate',
        response.headers.get('cache-control'))
    self.assertEqual('REMOTE_USER',
        response.headers.get('vary'))
    self.assertTrue('last-modified' in response.headers)
    self.assertEqual('text/xml; charset=utf-8',
        response.headers.get('content-type'))
    # check returned XML
    xml_fp = StringIO.StringIO()

    xml.dom.ext.PrettyPrint(xml.dom.ext.reader.Sax.FromXml(response.body),
        stream=xml_fp)
    xml_fp.seek(0)
    got_xml = xml_fp.read()
    expected_xml = """\
<?xml version='1.0' encoding='UTF-8'?>
<marshal>
  <dictionary id='i2'>
    <string>certificate</string>
    <string>%(instance_certificate)s</string>
    <string>key</string>
    <string>%(instance_key)s</string>
  </dictionary>
</marshal>
""" % dict(
  instance_certificate=self.start_requested_software_instance.getSslCertificate(),
  instance_key=self.start_requested_software_instance.getSslKey()
)
    self.assertEqual(expected_xml, got_xml,
        '\n'.join([q for q in difflib.unified_diff(expected_xml.split('\n'), got_xml.split('\n'))]))

  def test_getFullComputeNodeInformation(self):
    self._makeComplexComputeNode(with_slave=True)
    self.login(self.start_requested_software_instance.getUserId())
    response = self.portal_slap.getFullComputeNodeInformation(self.compute_node_id)
    self.assertEqual(200, response.status)
    self.assertEqual('public, max-age=1, stale-if-error=604800',
        response.headers.get('cache-control'))
    self.assertEqual('REMOTE_USER',
        response.headers.get('vary'))
    self.assertFalse('etag' in response.headers)
    self.assertEqual('text/xml; charset=utf-8',
        response.headers.get('content-type'))
    # check returned XML
    xml_fp = StringIO.StringIO()

    xml.dom.ext.PrettyPrint(xml.dom.ext.reader.Sax.FromXml(response.body),
        stream=xml_fp)
    xml_fp.seek(0)
    got_xml = xml_fp.read()
    expected_xml = """\
<?xml version='1.0' encoding='UTF-8'?>
<marshal>
  <object id='i2' module='slapos.slap.slap' class='Compute Node'>
    <tuple>
      <string>%(compute_node_id)s</string>
    </tuple>
    <dictionary id='i3'>
      <string>_compute_node_id</string>
      <string>%(compute_node_id)s</string>
      <string>_compute_partition_list</string>
      <list id='i4'>
        <object id='i5' module='slapos.slap.slap' class='ComputePartition'>
          <tuple>
            <string>%(compute_node_id)s</string>
            <string>partition1</string>
          </tuple>
          <dictionary id='i6'>
            <string>_access_status</string>
            <string>%(access_status)s</string>
            <string>_compute_node_id</string>
            <string>%(compute_node_id)s</string>
            <string>_connection_dict</string>
            <dictionary id='i7'/>
            <string>_filter_dict</string>
            <dictionary id='i8'>
              <string>paramé</string>
              <string>%(sla)s</string>
            </dictionary>
            <string>_instance_guid</string>
            <string>%(instance_guid)s</string>
            <string>_need_modification</string>
            <int>1</int>
            <string>_parameter_dict</string>
            <dictionary id='i9'>
              <string>full_ip_list</string>
              <list id='i10'/>
              <string>instance_title</string>
              <string>%(instance_title)s</string>
              <string>ip_list</string>
              <list id='i11'>
                <tuple>
                  <string/>
                  <string>ip_address_1</string>
                </tuple>
              </list>
              <string>paramé</string>
              <string>%(param)s</string>
              <string>root_instance_short_title</string>
              <string/>
              <string>root_instance_title</string>
              <string>%(root_instance_title)s</string>
              <string>slap_compute_node_id</string>
              <string>%(compute_node_id)s</string>
              <string>slap_compute_partition_id</string>
              <string>partition1</string>
              <string>slap_software_release_url</string>
              <string>%(software_release_url)s</string>
              <string>slap_software_type</string>
              <string>%(software_type)s</string>
              <string>slave_instance_list</string>
              <list id='i12'>
                <dictionary id='i13'>
                  <string>paramé</string>
                  <string>%(slave_1_param)s</string>
                  <string>slap_software_type</string>
                  <string>%(slave_1_software_type)s</string>
                  <string>slave_reference</string>
                  <string>%(slave_1_instance_guid)s</string>
                  <string>slave_title</string>
                  <string>%(slave_1_title)s</string>
                  <string>timestamp</string>
                  <int>%(timestamp)s</int>
                </dictionary>
              </list>
              <string>timestamp</string>
              <string>%(timestamp)s</string>
            </dictionary>
            <string>_partition_id</string>
            <string>partition1</string>
            <string>_request_dict</string>
            <none/>
            <string>_requested_state</string>
            <string>started</string>
            <string>_software_release_document</string>
            <object id='i14' module='slapos.slap.slap' class='SoftwareRelease'>
              <tuple>
                <string>%(software_release_url)s</string>
                <string>%(compute_node_id)s</string>
              </tuple>
              <dictionary id='i15'>
                <string>_compute_node_guid</string>
                <string>%(compute_node_id)s</string>
                <string>_software_instance_list</string>
                <list id='i16'/>
                <string>_software_release</string>
                <string>%(software_release_url)s</string>
              </dictionary>
            </object>
          </dictionary>
        </object>
      </list>
      <string>_software_release_list</string>
      <list id='i17'/>
    </dictionary>
  </object>
</marshal>
""" % dict(
    compute_node_id=self.compute_node_id,
    instance_guid=self.start_requested_software_instance.getReference(),
    instance_title=self.start_requested_software_instance.getTitle(),
    root_instance_title=self.start_requested_software_instance.getSpecialiseValue().getTitle(),
    software_release_url=self.start_requested_software_instance.getUrlString(),
    software_type=self.start_requested_software_instance.getSourceReference(),
    param=self.start_requested_software_instance.getInstanceXmlAsDict()['paramé'],
    sla=self.start_requested_software_instance.getSlaXmlAsDict()['paramé'],
    timestamp=int(self.start_requested_software_instance.getModificationDate()),
    slave_1_param=self.start_requested_slave_instance.getInstanceXmlAsDict()['paramé'],
    slave_1_software_type=self.start_requested_slave_instance.getSourceReference(),
    slave_1_instance_guid=self.start_requested_slave_instance.getReference(),
    slave_1_title=self.start_requested_slave_instance.getTitle(),
    access_status="#error no data found!",
)
    self.assertEqual(expected_xml, got_xml,
        '\n'.join([q for q in difflib.unified_diff(expected_xml.split('\n'), got_xml.split('\n'))]))

  def test_getComputePartitionStatus(self):
    self._makeComplexComputeNode()
    partition_id = self.start_requested_software_instance.getAggregateValue(
        portal_type='Compute Partition').getReference()
    created_at = rfc1123_date(DateTime())
    since = created_at
    self.login(self.start_requested_software_instance.getUserId())
    response = self.portal_slap.getComputePartitionStatus(self.compute_node_id,
      partition_id)
    self.assertEqual(200, response.status)
    self.assertEqual( 'public, max-age=60, stale-if-error=604800',
        response.headers.get('cache-control'))
    self.assertEqual('REMOTE_USER',
        response.headers.get('vary'))
    self.assertTrue('last-modified' in response.headers)
    self.assertEqual('text/xml; charset=utf-8',
        response.headers.get('content-type'))
    # check returned XML
    xml_fp = StringIO.StringIO()

    xml.dom.ext.PrettyPrint(xml.dom.ext.reader.Sax.FromXml(response.body),
        stream=xml_fp)
    xml_fp.seek(0)
    got_xml = xml_fp.read()
    expected_xml = """\
<?xml version='1.0' encoding='UTF-8'?>
<marshal>
  <dictionary id='i2'>
    <string>created_at</string>
    <string>%(created_at)s</string>
    <string>since</string>
    <string>%(since)s</string>
    <string>state</string>
    <string/>
    <string>text</string>
    <string>#error no data found for %(instance_guid)s</string>
    <string>user</string>
    <string>SlapOS Master</string>
  </dictionary>
</marshal>
""" % dict(
  created_at=created_at,
  since=since,
  instance_guid=self.start_requested_software_instance.getReference(),
)
    self.assertEqual(expected_xml, got_xml,
        '\n'.join([q for q in difflib.unified_diff(expected_xml.split('\n'), got_xml.split('\n'))]))

  def test_getComputePartitionStatus_visited(self):
    self._makeComplexComputeNode()
    partition_id = self.start_requested_software_instance.getAggregateValue(
        portal_type='Compute Partition').getReference()
    created_at = rfc1123_date(DateTime())
    since = created_at
    self.login(self.start_requested_software_instance.getUserId())
    self.portal_slap.registerComputePartition(self.compute_node_id, partition_id)
    response = self.portal_slap.getComputePartitionStatus(self.compute_node_id,
      partition_id)
    self.assertEqual(200, response.status)
    self.assertEqual( 'public, max-age=60, stale-if-error=604800',
        response.headers.get('cache-control'))
    self.assertEqual('REMOTE_USER',
        response.headers.get('vary'))
    self.assertTrue('last-modified' in response.headers)
    self.assertEqual('text/xml; charset=utf-8',
        response.headers.get('content-type'))
    # check returned XML
    xml_fp = StringIO.StringIO()

    xml.dom.ext.PrettyPrint(xml.dom.ext.reader.Sax.FromXml(response.body),
        stream=xml_fp)
    xml_fp.seek(0)
    got_xml = xml_fp.read()
    expected_xml = """\
<?xml version='1.0' encoding='UTF-8'?>
<marshal>
  <dictionary id='i2'>
    <string>created_at</string>
    <string>%(created_at)s</string>
    <string>since</string>
    <string>%(since)s</string>
    <string>state</string>
    <string/>
    <string>text</string>
    <string>#error no data found for %(instance_guid)s</string>
    <string>user</string>
    <string>SlapOS Master</string>
  </dictionary>
</marshal>
""" % dict(
  created_at=created_at,
  since=since,
  instance_guid=self.start_requested_software_instance.getReference(),
  compute_node_id=self.compute_node_id,
  partition_id=partition_id
)
    self.assertEqual(expected_xml, got_xml,
        '\n'.join([q for q in difflib.unified_diff(expected_xml.split('\n'), got_xml.split('\n'))]))

  def test_registerComputePartition_withSlave(self):
    self._makeComplexComputeNode(with_slave=True)
    partition_id = self.start_requested_software_instance.getAggregateValue(
        portal_type='Compute Partition').getReference()
    self.login(self.start_requested_software_instance.getUserId())
    response = self.portal_slap.registerComputePartition(self.compute_node_id, partition_id)
    self.assertEqual(200, response.status)
    self.assertEqual( 'public, max-age=1, stale-if-error=604800',
        response.headers.get('cache-control'))
    self.assertEqual('REMOTE_USER',
        response.headers.get('vary'))
    self.assertTrue('last-modified' in response.headers)
    self.assertEqual('text/xml; charset=utf-8',
        response.headers.get('content-type'))
    # check returned XML
    xml_fp = StringIO.StringIO()

    xml.dom.ext.PrettyPrint(xml.dom.ext.reader.Sax.FromXml(response.body),
        stream=xml_fp)
    xml_fp.seek(0)
    got_xml = xml_fp.read()
    expected_xml = """\
<?xml version='1.0' encoding='UTF-8'?>
<marshal>
  <object id='i2' module='slapos.slap.slap' class='ComputePartition'>
    <tuple>
      <string>%(compute_node_id)s</string>
      <string>partition1</string>
    </tuple>
    <dictionary id='i3'>
      <string>_compute_node_id</string>
      <string>%(compute_node_id)s</string>
      <string>_connection_dict</string>
      <dictionary id='i4'/>
      <string>_filter_dict</string>
      <dictionary id='i5'>
        <string>paramé</string>
        <string>%(sla)s</string>
      </dictionary>
      <string>_instance_guid</string>
      <string>%(instance_guid)s</string>
      <string>_need_modification</string>
      <int>1</int>
      <string>_parameter_dict</string>
      <dictionary id='i6'>
        <string>full_ip_list</string>
        <list id='i7'/>
        <string>instance_title</string>
        <string>%(instance_title)s</string>
        <string>ip_list</string>
        <list id='i8'>
          <tuple>
            <string/>
            <string>ip_address_1</string>
          </tuple>
        </list>
        <string>paramé</string>
        <string>%(param)s</string>
        <string>root_instance_short_title</string>
        <string/>
        <string>root_instance_title</string>
        <string>%(root_instance_title)s</string>
        <string>slap_compute_node_id</string>
        <string>%(compute_node_id)s</string>
        <string>slap_compute_partition_id</string>
        <string>partition1</string>
        <string>slap_software_release_url</string>
        <string>%(software_release_url)s</string>
        <string>slap_software_type</string>
        <string>%(software_type)s</string>
        <string>slave_instance_list</string>
        <list id='i9'>
          <dictionary id='i10'>
            <string>connection-parameter-hash</string>
            <string>4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945</string>
            <string>paramé</string>
            <string>%(slave_1_param)s</string>
            <string>slap_software_type</string>
            <string>%(slave_1_software_type)s</string>
            <string>slave_reference</string>
            <string>%(slave_1_instance_guid)s</string>
            <string>slave_title</string>
            <string>%(slave_1_title)s</string>
            <string>timestamp</string>
            <int>%(timestamp)s</int>
          </dictionary>
        </list>
        <string>timestamp</string>
        <string>%(timestamp)s</string>
      </dictionary>
      <string>_partition_id</string>
      <string>partition1</string>
      <string>_request_dict</string>
      <none/>
      <string>_requested_state</string>
      <string>started</string>
      <string>_software_release_document</string>
      <object id='i11' module='slapos.slap.slap' class='SoftwareRelease'>
        <tuple>
          <string>%(software_release_url)s</string>
          <string>%(compute_node_id)s</string>
        </tuple>
        <dictionary id='i12'>
          <string>_compute_node_guid</string>
          <string>%(compute_node_id)s</string>
          <string>_software_instance_list</string>
          <list id='i13'/>
          <string>_software_release</string>
          <string>%(software_release_url)s</string>
        </dictionary>
      </object>
      <string>_synced</string>
      <bool>1</bool>
    </dictionary>
  </object>
</marshal>
""" % dict(
  compute_node_id=self.compute_node_id,
  param=self.start_requested_software_instance.getInstanceXmlAsDict()['paramé'],
  sla=self.start_requested_software_instance.getSlaXmlAsDict()['paramé'],
  software_release_url=self.start_requested_software_instance.getUrlString(),
  timestamp=int(self.start_requested_software_instance.getModificationDate()),
  instance_guid=self.start_requested_software_instance.getReference(),
  instance_title=self.start_requested_software_instance.getTitle(),
  root_instance_title=self.start_requested_software_instance.getSpecialiseValue().getTitle(),
  software_type=self.start_requested_software_instance.getSourceReference(),
  slave_1_param=self.start_requested_slave_instance.getInstanceXmlAsDict()['paramé'],
  slave_1_software_type=self.start_requested_slave_instance.getSourceReference(),
  slave_1_instance_guid=self.start_requested_slave_instance.getReference(),
  slave_1_title=self.start_requested_slave_instance.getTitle(),
)
    self.assertEqual(expected_xml, got_xml,
        '\n'.join([q for q in difflib.unified_diff(expected_xml.split('\n'), got_xml.split('\n'))]))

  def test_registerComputePartition(self):
    self._makeComplexComputeNode()
    partition_id = self.start_requested_software_instance.getAggregateValue(
        portal_type='Compute Partition').getReference()
    self.login(self.start_requested_software_instance.getUserId())
    response = self.portal_slap.registerComputePartition(self.compute_node_id, partition_id)
    self.assertEqual(200, response.status)
    self.assertEqual( 'public, max-age=1, stale-if-error=604800',
        response.headers.get('cache-control'))
    self.assertEqual('REMOTE_USER',
        response.headers.get('vary'))
    self.assertTrue('last-modified' in response.headers)
    self.assertEqual('text/xml; charset=utf-8',
        response.headers.get('content-type'))
    # check returned XML
    xml_fp = StringIO.StringIO()

    xml.dom.ext.PrettyPrint(xml.dom.ext.reader.Sax.FromXml(response.body),
        stream=xml_fp)
    xml_fp.seek(0)
    got_xml = xml_fp.read()
    expected_xml = """\
<?xml version='1.0' encoding='UTF-8'?>
<marshal>
  <object id='i2' module='slapos.slap.slap' class='ComputePartition'>
    <tuple>
      <string>%(compute_node_id)s</string>
      <string>partition1</string>
    </tuple>
    <dictionary id='i3'>
      <string>_compute_node_id</string>
      <string>%(compute_node_id)s</string>
      <string>_connection_dict</string>
      <dictionary id='i4'/>
      <string>_filter_dict</string>
      <dictionary id='i5'>
        <string>paramé</string>
        <string>%(sla)s</string>
      </dictionary>
      <string>_instance_guid</string>
      <string>%(instance_guid)s</string>
      <string>_need_modification</string>
      <int>1</int>
      <string>_parameter_dict</string>
      <dictionary id='i6'>
        <string>full_ip_list</string>
        <list id='i7'/>
        <string>instance_title</string>
        <string>%(instance_title)s</string>
        <string>ip_list</string>
        <list id='i8'>
          <tuple>
            <string/>
            <string>ip_address_1</string>
          </tuple>
        </list>
        <string>paramé</string>
        <string>%(param)s</string>
        <string>root_instance_short_title</string>
        <string/>
        <string>root_instance_title</string>
        <string>%(root_instance_title)s</string>
        <string>slap_compute_node_id</string>
        <string>%(compute_node_id)s</string>
        <string>slap_compute_partition_id</string>
        <string>partition1</string>
        <string>slap_software_release_url</string>
        <string>%(software_release_url)s</string>
        <string>slap_software_type</string>
        <string>%(software_type)s</string>
        <string>slave_instance_list</string>
        <list id='i9'/>
        <string>timestamp</string>
        <string>%(timestamp)s</string>
      </dictionary>
      <string>_partition_id</string>
      <string>partition1</string>
      <string>_request_dict</string>
      <none/>
      <string>_requested_state</string>
      <string>started</string>
      <string>_software_release_document</string>
      <object id='i10' module='slapos.slap.slap' class='SoftwareRelease'>
        <tuple>
          <string>%(software_release_url)s</string>
          <string>%(compute_node_id)s</string>
        </tuple>
        <dictionary id='i11'>
          <string>_compute_node_guid</string>
          <string>%(compute_node_id)s</string>
          <string>_software_instance_list</string>
          <list id='i12'/>
          <string>_software_release</string>
          <string>%(software_release_url)s</string>
        </dictionary>
      </object>
      <string>_synced</string>
      <bool>1</bool>
    </dictionary>
  </object>
</marshal>
""" % dict(
  compute_node_id=self.compute_node_id,
  param=self.start_requested_software_instance.getInstanceXmlAsDict()['paramé'],
  sla=self.start_requested_software_instance.getSlaXmlAsDict()['paramé'],
  software_release_url=self.start_requested_software_instance.getUrlString(),
  timestamp=int(self.start_requested_software_instance.getModificationDate()),
  instance_guid=self.start_requested_software_instance.getReference(),
  instance_title=self.start_requested_software_instance.getTitle(),
  root_instance_title=self.start_requested_software_instance.getSpecialiseValue().getTitle(),
  software_type=self.start_requested_software_instance.getSourceReference()
)
    self.assertEqual(expected_xml, got_xml,
        '\n'.join([q for q in difflib.unified_diff(expected_xml.split('\n'), got_xml.split('\n'))]))

  def assertInstanceUpdateConnectionSimulator(self, args, kwargs):
    stored = eval(open(self.instance_update_connection_simulator).read()) #pylint: disable=eval-used
    # do the same translation magic as in workflow
    kwargs['connection_xml'] = kwargs.pop('connection_xml')
    self.assertEqual(stored,
      [{'recargs': args, 'reckwargs': kwargs,
      'recmethod': 'updateConnection'}])

  def test_setConnectionXml_withSlave(self):
    self._makeComplexComputeNode(with_slave=True)
    partition_id = self.start_requested_software_instance.getAggregateValue(
        portal_type='Compute Partition').getReference()
    slave_reference = self.start_requested_slave_instance.getReference()
    connection_xml = """<marshal>
  <dictionary id="i2">
    <string>p1é</string>
    <string>v1é</string>
    <string>p2é</string>
    <string>v2é</string>
  </dictionary>
</marshal>"""
    stored_xml = """<?xml version='1.0' encoding='utf-8'?>
<instance>
  <parameter id="p1é">v1é</parameter>
  <parameter id="p2é">v2é</parameter>
</instance>
"""
    self.login(self.start_requested_software_instance.getUserId())

    self.instance_update_connection_simulator = tempfile.mkstemp()[1]
    try:
      self.start_requested_slave_instance.updateConnection = Simulator(
        self.instance_update_connection_simulator, 'updateConnection')
      response = self.portal_slap.setComputePartitionConnectionXml(
        self.compute_node_id, partition_id, connection_xml, slave_reference)
      self.assertEqual('None', response)
      self.assertInstanceUpdateConnectionSimulator((),
          {'connection_xml': stored_xml})
    finally:
      if os.path.exists(self.instance_update_connection_simulator):
        os.unlink(self.instance_update_connection_simulator)

  def test_setConnectionXml(self):
    self._makeComplexComputeNode()
    partition_id = self.start_requested_software_instance.getAggregateValue(
        portal_type='Compute Partition').getReference()
    connection_xml = """<marshal>
  <dictionary id="i2">
    <string>p1é</string>
    <string>v1é</string>
    <string>p2é</string>
    <string>v2é</string>
  </dictionary>
</marshal>"""
    stored_xml = """<?xml version='1.0' encoding='utf-8'?>
<instance>
  <parameter id="p1é">v1é</parameter>
  <parameter id="p2é">v2é</parameter>
</instance>
"""
    self.login(self.start_requested_software_instance.getUserId())

    self.instance_update_connection_simulator = tempfile.mkstemp()[1]
    try:
      self.start_requested_software_instance.updateConnection = Simulator(
        self.instance_update_connection_simulator, 'updateConnection')
      response = self.portal_slap.setComputePartitionConnectionXml(
          self.compute_node_id, partition_id, connection_xml)
      self.assertEqual('None', response)
      self.assertInstanceUpdateConnectionSimulator((),
          {'connection_xml': stored_xml})
    finally:
      if os.path.exists(self.instance_update_connection_simulator):
        os.unlink(self.instance_update_connection_simulator)

  def test_softwareInstanceError(self):
    self._makeComplexComputeNode()
    partition_id = self.start_requested_software_instance.getAggregateValue(
        portal_type='Compute Partition').getReference()
    self.login(self.start_requested_software_instance.getUserId())
    error_log = 'The error'
    response = self.portal_slap.softwareInstanceError(self.compute_node_id,
      partition_id, error_log)
    self.assertEqual('None', response)
    created_at = rfc1123_date(DateTime())
    since = created_at
    response = self.portal_slap.getComputePartitionStatus(self.compute_node_id,
      partition_id)
    # check returned XML
    xml_fp = StringIO.StringIO()

    xml.dom.ext.PrettyPrint(xml.dom.ext.reader.Sax.FromXml(response.body),
        stream=xml_fp)
    xml_fp.seek(0)
    got_xml = xml_fp.read()
    expected_xml = """\
<?xml version='1.0' encoding='UTF-8'?>
<marshal>
  <dictionary id='i2'>
    <string>created_at</string>
    <string>%(created_at)s</string>
    <string>since</string>
    <string>%(since)s</string>
    <string>state</string>
    <string/>
    <string>text</string>
    <string>#error while instanciating: The error</string>
    <string>user</string>
    <string>%(instance_guid)s</string>
  </dictionary>
</marshal>
""" % dict(
  created_at=created_at,
  since = since,
  instance_guid=self.start_requested_software_instance.getReference(),
)
    self.assertEqual(expected_xml, got_xml,
        '\n'.join([q for q in difflib.unified_diff(expected_xml.split('\n'), got_xml.split('\n'))]))

  def test_softwareInstanceError_twice(self):
    self._makeComplexComputeNode()
    partition_id = self.start_requested_software_instance.getAggregateValue(
        portal_type='Compute Partition').getReference()
    self.login(self.start_requested_software_instance.getUserId())
    error_log = 'The error'
    response = self.portal_slap.softwareInstanceError(self.compute_node_id,
      partition_id, error_log)
    self.assertEqual('None', response)
    created_at = rfc1123_date(DateTime())
    since = created_at
    response = self.portal_slap.getComputePartitionStatus(self.compute_node_id,
      partition_id)
    # check returned XML
    xml_fp = StringIO.StringIO()

    xml.dom.ext.PrettyPrint(xml.dom.ext.reader.Sax.FromXml(response.body),
        stream=xml_fp)
    xml_fp.seek(0)
    got_xml = xml_fp.read()
    expected_xml = """\
<?xml version='1.0' encoding='UTF-8'?>
<marshal>
  <dictionary id='i2'>
    <string>created_at</string>
    <string>%(created_at)s</string>
    <string>since</string>
    <string>%(since)s</string>
    <string>state</string>
    <string/>
    <string>text</string>
    <string>#error while instanciating: The error</string>
    <string>user</string>
    <string>%(instance_guid)s</string>
  </dictionary>
</marshal>
""" % dict(
  created_at=created_at,
  since = since,
  instance_guid=self.start_requested_software_instance.getReference(),
)
    self.assertEqual(expected_xml, got_xml,
        '\n'.join([q for q in difflib.unified_diff(expected_xml.split('\n'), got_xml.split('\n'))]))

    self.unpinDateTime()
    time.sleep(1)
    self.pinDateTime(DateTime())

    response = self.portal_slap.softwareInstanceError(self.compute_node_id,
      partition_id, error_log)
    self.assertEqual('None', response)
    ncreated_at = rfc1123_date(DateTime())
    response = self.portal_slap.getComputePartitionStatus(self.compute_node_id,
      partition_id)
    # check returned XML
    xml_fp = StringIO.StringIO()

    xml.dom.ext.PrettyPrint(xml.dom.ext.reader.Sax.FromXml(response.body),
        stream=xml_fp)
    xml_fp.seek(0)
    got_xml = xml_fp.read()

    self.assertNotEqual(created_at, ncreated_at)
    self.assertNotEqual(since, ncreated_at)
    self.assertEqual(since, created_at)
    
    expected_xml = """\
<?xml version='1.0' encoding='UTF-8'?>
<marshal>
  <dictionary id='i2'>
    <string>created_at</string>
    <string>%(created_at)s</string>
    <string>since</string>
    <string>%(since)s</string>
    <string>state</string>
    <string/>
    <string>text</string>
    <string>#error while instanciating: The error</string>
    <string>user</string>
    <string>%(instance_guid)s</string>
  </dictionary>
</marshal>
""" % dict(
  created_at=ncreated_at,
  since = since,
  instance_guid=self.start_requested_software_instance.getReference(),
)
    self.assertEqual(expected_xml, got_xml,
        '\n'.join([q for q in difflib.unified_diff(expected_xml.split('\n'), got_xml.split('\n'))]))

  def assertInstanceBangSimulator(self, args, kwargs):
    stored = eval(open(self.instance_bang_simulator).read()) #pylint: disable=eval-used
    # do the same translation magic as in workflow
    kwargs['comment'] = kwargs.pop('comment')
    self.assertEqual(stored,
      [{'recargs': args, 'reckwargs': kwargs,
      'recmethod': 'bang'}])

  def test_softwareInstanceBang(self):
    self._makeComplexComputeNode()
    self.instance_bang_simulator = tempfile.mkstemp()[1]
    try:
      partition_id = self.start_requested_software_instance.getAggregateValue(
          portal_type='Compute Partition').getReference()
      self.login(self.start_requested_software_instance.getUserId())
      self.start_requested_software_instance.bang = Simulator(
        self.instance_bang_simulator, 'bang')
      error_log = 'Please bang me'
      response = self.portal_slap.softwareInstanceBang(self.compute_node_id,
        partition_id, error_log)
      self.assertEqual('OK', response)
      created_at = rfc1123_date(DateTime())
      since = created_at
      response = self.portal_slap.getComputePartitionStatus(self.compute_node_id,
        partition_id)
      # check returned XML
      xml_fp = StringIO.StringIO()

      xml.dom.ext.PrettyPrint(xml.dom.ext.reader.Sax.FromXml(response.body),
          stream=xml_fp)
      xml_fp.seek(0)
      got_xml = xml_fp.read()
      expected_xml = """\
<?xml version='1.0' encoding='UTF-8'?>
<marshal>
  <dictionary id='i2'>
    <string>created_at</string>
    <string>%(created_at)s</string>
    <string>since</string>
    <string>%(since)s</string>
    <string>state</string>
    <string/>
    <string>text</string>
    <string>#error bang called</string>
    <string>user</string>
    <string>%(instance_guid)s</string>
  </dictionary>
</marshal>
""" % dict(
    created_at=created_at,
    since=since,
    instance_guid=self.start_requested_software_instance.getReference(),
  )
      self.assertEqual(expected_xml, got_xml,
          '\n'.join([q for q in difflib.unified_diff(expected_xml.split('\n'), got_xml.split('\n'))]))
      self.assertInstanceBangSimulator((), {'comment': error_log, 'bang_tree': True})
    finally:
      if os.path.exists(self.instance_bang_simulator):
        os.unlink(self.instance_bang_simulator)
      
  def assertInstanceRenameSimulator(self, args, kwargs):
    stored = eval(open(self.instance_rename_simulator).read()) #pylint: disable=eval-used
    # do the same translation magic as in workflow
    self.assertEqual(stored,
      [{'recargs': args, 'reckwargs': kwargs,
      'recmethod': 'rename'}])

  def test_softwareInstanceRename(self):
    self._makeComplexComputeNode()
    self.instance_rename_simulator = tempfile.mkstemp()[1]
    try:
      partition_id = self.start_requested_software_instance.getAggregateValue(
          portal_type='Compute Partition').getReference()
      self.login(self.start_requested_software_instance.getUserId())
      self.start_requested_software_instance.rename = Simulator(
        self.instance_rename_simulator, 'rename')
      new_name = 'new me'
      response = self.portal_slap.softwareInstanceRename(new_name, self.compute_node_id,
        partition_id)
      self.assertEqual('None', response)
      self.assertInstanceRenameSimulator((), {
          'comment': 'Rename %s into %s' % (self.start_requested_software_instance.getTitle(),
            new_name), 'new_name': new_name})
    finally:
      if os.path.exists(self.instance_rename_simulator):
        os.unlink(self.instance_rename_simulator)
      
  def test_destroyedComputePartition(self):
    self._makeComplexComputeNode()
    partition_id = self.destroy_requested_software_instance.getAggregateValue(
        portal_type='Compute Partition').getReference()
    self.login(self.destroy_requested_software_instance.getUserId())
    response = self.portal_slap.destroyedComputePartition(self.compute_node_id,
      partition_id)
    self.assertEqual('None', response)
    self.assertEqual('invalidated',
        self.destroy_requested_software_instance.getValidationState())
    self.assertEqual(None, self.destroy_requested_software_instance.getSslKey())
    self.assertEqual(None, self.destroy_requested_software_instance.getSslCertificate())

  def assertInstanceRequestSimulator(self, args, kwargs):
    stored = eval(open(self.instance_request_simulator).read()) #pylint: disable=eval-used
    # do the same translation magic as in workflow
    self.assertEqual(stored,
      [{'recargs': args, 'reckwargs': kwargs,
      'recmethod': 'requestInstance'}])

  def test_request_withSlave(self):
    self._makeComplexComputeNode()
    self.instance_request_simulator = tempfile.mkstemp()[1]
    try:
      partition_id = self.start_requested_software_instance.getAggregateValue(
          portal_type='Compute Partition').getReference()
      self.login(self.start_requested_software_instance.getUserId())
      self.start_requested_software_instance.requestInstance = Simulator(
        self.instance_request_simulator, 'requestInstance')
      response = self.portal_slap.requestComputePartition(
          compute_node_id=self.compute_node_id,
          compute_partition_id=partition_id,
          software_release='req_release',
          software_type='req_type',
          partition_reference='req_reference',
          partition_parameter_xml='<marshal><dictionary id="i2"/></marshal>',
          filter_xml='<marshal><dictionary id="i2"/></marshal>',
          state='<marshal><string>started</string></marshal>',
          shared_xml='<marshal><bool>1</bool></marshal>',
          )
      self.assertEqual(408, response.status)
      self.assertEqual('private',
          response.headers.get('cache-control'))
      self.assertInstanceRequestSimulator((), {
          'instance_xml': "<?xml version='1.0' encoding='utf-8'?>\n<instance/>\n",
          'software_title': 'req_reference',
          'software_release': 'req_release',
          'state': 'started',
          'sla_xml': "<?xml version='1.0' encoding='utf-8'?>\n<instance/>\n",
          'software_type': 'req_type',
          'shared': True})
    finally:
      if os.path.exists(self.instance_request_simulator):
        os.unlink(self.instance_request_simulator)

  def test_request(self):
    self._makeComplexComputeNode()
    self.instance_request_simulator = tempfile.mkstemp()[1]
    try:
      partition_id = self.start_requested_software_instance.getAggregateValue(
          portal_type='Compute Partition').getReference()
      self.login(self.start_requested_software_instance.getUserId())
      self.start_requested_software_instance.requestInstance = Simulator(
        self.instance_request_simulator, 'requestInstance')
      response = self.portal_slap.requestComputePartition(
          compute_node_id=self.compute_node_id,
          compute_partition_id=partition_id,
          software_release='req_release',
          software_type='req_type',
          partition_reference='req_reference',
          partition_parameter_xml='<marshal><dictionary id="i2"/></marshal>',
          filter_xml='<marshal><dictionary id="i2"/></marshal>',
          state='<marshal><string>started</string></marshal>',
          shared_xml='<marshal><bool>0</bool></marshal>',
          )
      self.assertEqual(408, response.status)
      self.assertEqual('private',
          response.headers.get('cache-control'))
      self.assertInstanceRequestSimulator((), {
          'instance_xml': "<?xml version='1.0' encoding='utf-8'?>\n<instance/>\n",
          'software_title': 'req_reference',
          'software_release': 'req_release',
          'state': 'started',
          'sla_xml': "<?xml version='1.0' encoding='utf-8'?>\n<instance/>\n",
          'software_type': 'req_type',
          'shared': False})
    finally:
      if os.path.exists(self.instance_request_simulator):
        os.unlink(self.instance_request_simulator)

  def test_request_stopped(self):
    self._makeComplexComputeNode()
    self.instance_request_simulator = tempfile.mkstemp()[1]
    try:
      partition_id = self.stop_requested_software_instance.getAggregateValue(
          portal_type='Compute Partition').getReference()
      self.login(self.stop_requested_software_instance.getUserId())
      self.stop_requested_software_instance.requestInstance = Simulator(
        self.instance_request_simulator, 'requestInstance')
      response = self.portal_slap.requestComputePartition(
          compute_node_id=self.compute_node_id,
          compute_partition_id=partition_id,
          software_release='req_release',
          software_type='req_type',
          partition_reference='req_reference',
          partition_parameter_xml='<marshal><dictionary id="i2"/></marshal>',
          filter_xml='<marshal><dictionary id="i2"/></marshal>',
          state='<marshal><string>started</string></marshal>',
          shared_xml='<marshal><bool>0</bool></marshal>',
          )
      self.assertEqual(408, response.status)
      self.assertEqual('private',
          response.headers.get('cache-control'))
      self.assertInstanceRequestSimulator((), {
          'instance_xml': "<?xml version='1.0' encoding='utf-8'?>\n<instance/>\n",
          'software_title': 'req_reference',
          'software_release': 'req_release',
          'state': 'stopped',
          'sla_xml': "<?xml version='1.0' encoding='utf-8'?>\n<instance/>\n",
          'software_type': 'req_type',
          'shared': False})
    finally:
      if os.path.exists(self.instance_request_simulator):
        os.unlink(self.instance_request_simulator)

  def test_updateInstanceSuccessorList(self):
    self._makeComplexComputeNode()

    partition_id = self.start_requested_software_instance.getAggregateValue(
        portal_type='Compute Partition').getReference()
    self.login(self.start_requested_software_instance.getUserId())

    # Atach two software instances
    instance_kw = dict(
      software_release='http://a.release',
      software_type='type',
      instance_xml=self.generateSafeXml(),
      sla_xml=self.generateSafeXml(),
      shared=False,
      software_title='Instance0',
      state='started'
    )
    self.start_requested_software_instance.requestInstance(**instance_kw)
    instance_kw['software_title'] = 'Instance1'
    self.start_requested_software_instance.requestInstance(**instance_kw)
    self.tic()

    self.assertEqual(len(self.start_requested_software_instance.getSuccessorList()), 2)
    self.assertSameSet(['Instance0', 'Instance1'],
            self.start_requested_software_instance.getSuccessorTitleList())

    # Update with no changes
    instance_list_xml = """
<marshal>
  <list id="i2"><string>Instance0</string><string>Instance1</string></list>
</marshal>"""
    self.portal_slap.updateComputePartitionRelatedInstanceList(
        compute_node_id=self.compute_node_id,
        compute_partition_id=partition_id,
        instance_reference_xml=instance_list_xml)
    self.tic()
    self.assertSameSet(['Instance0', 'Instance1'],
            self.start_requested_software_instance.getSuccessorTitleList())

    # Update Instance0 was not requested
    instance_list_xml = """
<marshal>
  <list id="i2"><string>Instance1</string></list>
</marshal>"""
    self.portal_slap.updateComputePartitionRelatedInstanceList(
        compute_node_id=self.compute_node_id,
        compute_partition_id=partition_id,
        instance_reference_xml=instance_list_xml)
    self.tic()
    self.assertSameSet(['Instance1'],
            self.start_requested_software_instance.getSuccessorTitleList())

  def test_updateInstanceSuccessorList_one_child(self):
    self._makeComplexComputeNode()

    partition_id = self.start_requested_software_instance.getAggregateValue(
        portal_type='Compute Partition').getReference()
    self.login(self.start_requested_software_instance.getUserId())

    # Atach one software instance
    instance_kw = dict(
      software_release='http://a.release',
      software_type='type',
      instance_xml=self.generateSafeXml(),
      sla_xml=self.generateSafeXml(),
      shared=False,
      software_title='Instance0',
      state='started'
    )
    self.start_requested_software_instance.requestInstance(**instance_kw)
    self.tic()

    self.assertEqual(len(self.start_requested_software_instance.getSuccessorList()), 1)
    self.assertSameSet(['Instance0'],
            self.start_requested_software_instance.getSuccessorTitleList())

    instance_list_xml = '<marshal><list id="i2" /></marshal>'
    self.portal_slap.updateComputePartitionRelatedInstanceList(
        compute_node_id=self.compute_node_id,
        compute_partition_id=partition_id,
        instance_reference_xml=instance_list_xml)
    self.tic()
    self.assertEqual([],
              self.start_requested_software_instance.getSuccessorTitleList())

  def test_updateInstanceSuccessorList_no_child(self):
    self._makeComplexComputeNode()

    partition_id = self.start_requested_software_instance.getAggregateValue(
        portal_type='Compute Partition').getReference()
    self.login(self.start_requested_software_instance.getUserId())

    self.assertEqual([],
            self.start_requested_software_instance.getSuccessorTitleList())

    instance_list_xml = '<marshal><list id="i2" /></marshal>'
    self.portal_slap.updateComputePartitionRelatedInstanceList(
        compute_node_id=self.compute_node_id,
        compute_partition_id=partition_id,
        instance_reference_xml=instance_list_xml)
    self.tic()
    self.assertEqual([],
              self.start_requested_software_instance.getSuccessorTitleList())

    # Try with something that doesn't exist
    instance_list_xml = """
<marshal>
<list id="i2"><string>instance0</string></list>
</marshal>"""
    self.portal_slap.updateComputePartitionRelatedInstanceList(
        compute_node_id=self.compute_node_id,
        compute_partition_id=partition_id,
        instance_reference_xml=instance_list_xml)
    self.tic()
    self.assertEqual([],
              self.start_requested_software_instance.getSuccessorTitleList())

  def test_stoppedComputePartition(self):
    self._makeComplexComputeNode()
    partition_id = self.start_requested_software_instance.getAggregateValue(
        portal_type='Compute Partition').getReference()
    self.login(self.start_requested_software_instance.getUserId())
    response = self.portal_slap.stoppedComputePartition(self.compute_node_id,
      partition_id)
    self.assertEqual('None', response)
    created_at = rfc1123_date(DateTime())
    since = created_at
    response = self.portal_slap.getComputePartitionStatus(self.compute_node_id,
      partition_id)
    # check returned XML
    xml_fp = StringIO.StringIO()

    xml.dom.ext.PrettyPrint(xml.dom.ext.reader.Sax.FromXml(response.body),
        stream=xml_fp)
    xml_fp.seek(0)
    got_xml = xml_fp.read()
    expected_xml = """\
<?xml version='1.0' encoding='UTF-8'?>
<marshal>
  <dictionary id='i2'>
    <string>created_at</string>
    <string>%(created_at)s</string>
    <string>since</string>
    <string>%(since)s</string>
    <string>state</string>
    <string>stopped</string>
    <string>text</string>
    <string>#access Instance correctly stopped</string>
    <string>user</string>
    <string>%(instance_guid)s</string>
  </dictionary>
</marshal>
""" % dict(
  created_at=created_at,
  since=since,
  instance_guid=self.start_requested_software_instance.getReference(),
)
    self.assertEqual(expected_xml, got_xml,
        '\n'.join([q for q in difflib.unified_diff(expected_xml.split('\n'), got_xml.split('\n'))]))

  def test_startedComputePartition(self):
    self._makeComplexComputeNode()
    partition_id = self.start_requested_software_instance.getAggregateValue(
        portal_type='Compute Partition').getReference()
    self.login(self.start_requested_software_instance.getUserId())
    response = self.portal_slap.startedComputePartition(self.compute_node_id,
      partition_id)
    self.assertEqual('None', response)
    created_at = rfc1123_date(DateTime())
    since = created_at
    response = self.portal_slap.getComputePartitionStatus(self.compute_node_id,
      partition_id)
    # check returned XML
    xml_fp = StringIO.StringIO()

    xml.dom.ext.PrettyPrint(xml.dom.ext.reader.Sax.FromXml(response.body),
        stream=xml_fp)
    xml_fp.seek(0)
    got_xml = xml_fp.read()
    expected_xml = """\
<?xml version='1.0' encoding='UTF-8'?>
<marshal>
  <dictionary id='i2'>
    <string>created_at</string>
    <string>%(created_at)s</string>
    <string>since</string>
    <string>%(since)s</string>
    <string>state</string>
    <string>started</string>
    <string>text</string>
    <string>#access Instance correctly started</string>
    <string>user</string>
    <string>%(instance_guid)s</string>
  </dictionary>
</marshal>
""" % dict(
  created_at=created_at,
  since=since,
  instance_guid=self.start_requested_software_instance.getReference(),
)
    self.assertEqual(expected_xml, got_xml,
        '\n'.join([q for q in difflib.unified_diff(expected_xml.split('\n'), got_xml.split('\n'))]))

  def test_getSoftwareReleaseListFromSoftwareProduct(self):
    new_id = self.generateNewId()
    software_product = self._makeSoftwareProduct(new_id)
    # 2 published software releases
    software_release1 = self._makeSoftwareRelease(new_id)
    software_release2 = self._makeSoftwareRelease(self.generateNewId())
    software_release1.publish()
    software_release2.publish()
    # 1 released software release, should not appear
    software_release3 = self._makeSoftwareRelease(new_id)
    self.assertTrue(software_release3.getValidationState() == 'released')
    software_release1.edit(
        aggregate_value=software_product.getRelativeUrl(),
        url_string='http://example.org/1.cfg',
        effective_date=DateTime()
    )
    software_release2.edit(
        aggregate_value=software_product.getRelativeUrl(),
        url_string='http://example.org/2.cfg',
        effective_date=DateTime()
    )
    software_release3.edit(
        aggregate_value=software_product.getRelativeUrl(),
        url_string='http://example.org/3.cfg'
    )
    self.tic()

    response = self.portal_slap.getSoftwareReleaseListFromSoftwareProduct(
        software_product.getReference())
    # check returned XML
    xml_fp = StringIO.StringIO()
    xml.dom.ext.PrettyPrint(xml.dom.ext.reader.Sax.FromXml(response),
        stream=xml_fp)
    xml_fp.seek(0)
    got_xml = xml_fp.read()
    expected_xml = """\
<?xml version='1.0' encoding='UTF-8'?>
<marshal>
  <list id='i2'>
    <string>%s</string>
    <string>%s</string>
  </list>
</marshal>
""" % (software_release2.getUrlString(), software_release1.getUrlString())
    self.assertEqual(expected_xml, got_xml,
        '\n'.join([q for q in difflib.unified_diff(expected_xml.split('\n'), got_xml.split('\n'))]))
  
  def test_getSoftwareReleaseListFromSoftwareProduct_effectiveDate(self):
    new_id = self.generateNewId()
    software_product = self._makeSoftwareProduct(new_id)
    # 3 published software releases
    software_release1 = self._makeSoftwareRelease(new_id)
    software_release2 = self._makeSoftwareRelease(self.generateNewId())
    software_release3 = self._makeSoftwareRelease(self.generateNewId())
    software_release1.publish()
    software_release2.publish()
    software_release3.publish()
    software_release1.edit(
        aggregate_value=software_product.getRelativeUrl(),
        url_string='http://example.org/1.cfg',
        effective_date=(DateTime() - 1)
    )
    # Should not be considered yet!
    software_release2.edit(
        aggregate_value=software_product.getRelativeUrl(),
        url_string='http://example.org/2.cfg',
        effective_date=(DateTime() + 1)
    )
    software_release3.edit(
        aggregate_value=software_product.getRelativeUrl(),
        url_string='http://example.org/3.cfg',
        effective_date=DateTime()
    )
    self.tic()

    response = self.portal_slap.getSoftwareReleaseListFromSoftwareProduct(
        software_product.getReference())
    # check returned XML
    xml_fp = StringIO.StringIO()
    xml.dom.ext.PrettyPrint(xml.dom.ext.reader.Sax.FromXml(response),
        stream=xml_fp)
    xml_fp.seek(0)
    got_xml = xml_fp.read()
    expected_xml = """\
<?xml version='1.0' encoding='UTF-8'?>
<marshal>
  <list id='i2'>
    <string>%s</string>
    <string>%s</string>
    <string>%s</string>
  </list>
</marshal>
""" % (software_release3.getUrlString(), software_release1.getUrlString(),
        software_release2.getUrlString())
    self.assertEqual(expected_xml, got_xml,
        '\n'.join([q for q in difflib.unified_diff(expected_xml.split('\n'), got_xml.split('\n'))]))

  def test_getSoftwareReleaseListFromSoftwareProduct_emptySoftwareProduct(self):
    new_id = self.generateNewId()
    software_product = self._makeSoftwareProduct(new_id)

    response = self.portal_slap.getSoftwareReleaseListFromSoftwareProduct(
        software_product.getReference())
    # check returned XML
    xml_fp = StringIO.StringIO()
    xml.dom.ext.PrettyPrint(xml.dom.ext.reader.Sax.FromXml(response),
        stream=xml_fp)
    xml_fp.seek(0)
    got_xml = xml_fp.read()
    expected_xml = """\
<?xml version='1.0' encoding='UTF-8'?>
<marshal>
  <list id='i2'/>
</marshal>
""" 
    self.assertEqual(expected_xml, got_xml,
        '\n'.join([q for q in difflib.unified_diff(expected_xml.split('\n'), got_xml.split('\n'))]))

  def test_getSoftwareReleaseListFromSoftwareProduct_NoSoftwareProduct(self):
    response = self.portal_slap.getSoftwareReleaseListFromSoftwareProduct(
        'Can I has a nonexistent software product?')
    # check returned XML
    xml_fp = StringIO.StringIO()
    xml.dom.ext.PrettyPrint(xml.dom.ext.reader.Sax.FromXml(response),
        stream=xml_fp)
    xml_fp.seek(0)
    got_xml = xml_fp.read()
    expected_xml = """\
<?xml version='1.0' encoding='UTF-8'?>
<marshal>
  <list id='i2'/>
</marshal>
""" 
    self.assertEqual(expected_xml, got_xml,
        '\n'.join([q for q in difflib.unified_diff(expected_xml.split('\n'), got_xml.split('\n'))]))

  def test_getSoftwareReleaseListFromSoftwareProduct_fromUrl(self):
    new_id = self.generateNewId()
    software_product = self._makeSoftwareProduct(new_id)
    # 2 published software releases
    software_release1 = self._makeSoftwareRelease(new_id)
    software_release2 = self._makeSoftwareRelease(self.generateNewId())
    software_release1.publish()
    software_release2.publish()
    # 1 released software release, should not appear
    software_release3 = self._makeSoftwareRelease(new_id)
    self.assertTrue(software_release3.getValidationState() == 'released')
    software_release1.edit(
        aggregate_value=software_product.getRelativeUrl(),
        url_string='http://example.org/1.cfg'
    )
    software_release2.edit(
        aggregate_value=software_product.getRelativeUrl(),
        url_string='http://example.org/2.cfg'
    )
    software_release3.edit(
        aggregate_value=software_product.getRelativeUrl(),
        url_string='http://example.org/3.cfg'
    )
    self.tic()

    response = self.portal_slap.getSoftwareReleaseListFromSoftwareProduct(
        software_release_url=software_release2.getUrlString())
    # check returned XML
    xml_fp = StringIO.StringIO()
    xml.dom.ext.PrettyPrint(xml.dom.ext.reader.Sax.FromXml(response),
        stream=xml_fp)
    xml_fp.seek(0)
    got_xml = xml_fp.read()
    expected_xml = """\
<?xml version='1.0' encoding='UTF-8'?>
<marshal>
  <list id='i2'>
    <string>%s</string>
    <string>%s</string>
  </list>
</marshal>
""" % (software_release2.getUrlString(), software_release1.getUrlString())
    self.assertEqual(expected_xml, got_xml,
        '\n'.join([q for q in difflib.unified_diff(expected_xml.split('\n'), got_xml.split('\n'))]))


class TestSlapOSSlapToolPersonAccess(TestSlapOSSlapToolMixin):
  def afterSetUp(self):
    password = "%s-1Aa$" % self.generateNewId() 
    reference = 'test_%s' % self.generateNewId()
    person = self.portal.person_module.newContent(portal_type='Person',
      title=reference,
      reference=reference)
    person.newContent(portal_type='Assignment', role='member').open()
    person.newContent(portal_type='ERP5 Login',
      reference=reference, password=password).validate()

    self.commit()
    self.person = person
    self.person_reference = person.getReference()
    self.person_user_id = person.getUserId()
    TestSlapOSSlapToolMixin.afterSetUp(self)

  def test_not_accessed_getComputeNodeStatus(self):
    self.login(self.person_user_id)
    created_at = rfc1123_date(DateTime())
    since = created_at
    response = self.portal_slap.getComputeNodeStatus(self.compute_node_id)
    self.assertEqual(200, response.status)
    self.assertEqual('public, max-age=60, stale-if-error=604800',
        response.headers.get('cache-control'))
    self.assertEqual('REMOTE_USER',
        response.headers.get('vary'))
    self.assertTrue('last-modified' in response.headers)
    self.assertEqual('text/xml; charset=utf-8',
        response.headers.get('content-type'))
    # check returned XML
    xml_fp = StringIO.StringIO()

    xml.dom.ext.PrettyPrint(xml.dom.ext.reader.Sax.FromXml(response.body),
        stream=xml_fp)
    xml_fp.seek(0)
    got_xml = xml_fp.read()

    expected_xml = """\
<?xml version='1.0' encoding='UTF-8'?>
<marshal>
  <dictionary id='i2'>
    <string>created_at</string>
    <string>%(created_at)s</string>
    <string>since</string>
    <string>%(since)s</string>
    <string>state</string>
    <string/>
    <string>text</string>
    <string>#error no data found for %(compute_node_id)s</string>
    <string>user</string>
    <string>SlapOS Master</string>
  </dictionary>
</marshal>
""" % dict(
  created_at=created_at,
  since=since,
  compute_node_id=self.compute_node_id
)
    self.assertEqual(expected_xml, got_xml,
        '\n'.join([q for q in difflib.unified_diff(expected_xml.split('\n'), got_xml.split('\n'))]))

  def test_accessed_getComputeNodeStatus(self):
    self.login(self.compute_node_user_id)
    self.portal_slap.getFullComputeNodeInformation(self.compute_node_id)
    self.login(self.person_user_id)
    created_at = rfc1123_date(DateTime())
    since = created_at
    response = self.portal_slap.getComputeNodeStatus(self.compute_node_id)
    self.assertEqual(200, response.status)
    self.assertEqual('public, max-age=60, stale-if-error=604800',
        response.headers.get('cache-control'))
    self.assertEqual('REMOTE_USER',
        response.headers.get('vary'))
    self.assertTrue('last-modified' in response.headers)
    self.assertEqual('text/xml; charset=utf-8',
        response.headers.get('content-type'))

    # check returned XML
    xml_fp = StringIO.StringIO()

    xml.dom.ext.PrettyPrint(xml.dom.ext.reader.Sax.FromXml(response.body),
        stream=xml_fp)
    xml_fp.seek(0)
    got_xml = xml_fp.read()

    expected_xml = """\
<?xml version='1.0' encoding='UTF-8'?>
<marshal>
  <dictionary id='i2'>
    <string>created_at</string>
    <string>%(created_at)s</string>
    <string>since</string>
    <string>%(since)s</string>
    <string>state</string>
    <string/>
    <string>text</string>
    <string>#access %(compute_node_id)s</string>
    <string>user</string>
    <string>%(compute_node_id)s</string>
  </dictionary>
</marshal>
""" % dict(
  created_at=created_at,
  since=since,
  compute_node_id=self.compute_node_id
)
    self.assertEqual(expected_xml, got_xml,
        '\n'.join([q for q in difflib.unified_diff(expected_xml.split('\n'), got_xml.split('\n'))]))

  def assertComputeNodeBangSimulator(self, args, kwargs):
    stored = eval(open(self.compute_node_bang_simulator).read()) #pylint: disable=eval-used
    # do the same translation magic as in workflow
    kwargs['comment'] = kwargs.pop('comment')
    self.assertEqual(stored,
      [{'recargs': args, 'reckwargs': kwargs,
      'recmethod': 'reportComputeNodeBang'}])

  def test_compute_nodeBang(self):
    self.login(self.person_user_id)
    self.compute_node_bang_simulator = tempfile.mkstemp()[1]
    try:
      self.compute_node.reportComputeNodeBang = Simulator(
        self.compute_node_bang_simulator, 'reportComputeNodeBang')
      error_log = 'Please bang me'
      response = self.portal_slap.compute_nodeBang(self.compute_node_id,
        error_log)
      self.assertEqual('None', response)
      created_at = rfc1123_date(DateTime())
      since = created_at
      response = self.portal_slap.getComputeNodeStatus(self.compute_node_id)
      # check returned XML
      xml_fp = StringIO.StringIO()

      xml.dom.ext.PrettyPrint(xml.dom.ext.reader.Sax.FromXml(response.body),
          stream=xml_fp)
      xml_fp.seek(0)
      got_xml = xml_fp.read()
      expected_xml = """\
<?xml version='1.0' encoding='UTF-8'?>
<marshal>
  <dictionary id='i2'>
    <string>created_at</string>
    <string>%(created_at)s</string>
    <string>since</string>
    <string>%(since)s</string>
    <string>state</string>
    <string/>
    <string>text</string>
    <string>#error bang</string>
    <string>user</string>
    <string>%(person_reference)s</string>
  </dictionary>
</marshal>
""" % dict(
    created_at=created_at,
    since=since,
    person_reference=self.person_reference,
  )
      self.assertEqual(expected_xml, got_xml,
          '\n'.join([q for q in difflib.unified_diff(expected_xml.split('\n'), got_xml.split('\n'))]))
      self.assertComputeNodeBangSimulator((), {'comment': error_log})
    finally:
      if os.path.exists(self.compute_node_bang_simulator):
        os.unlink(self.compute_node_bang_simulator)

  def test_getComputePartitionStatus(self):
    self._makeComplexComputeNode()
    self.login(self.person_user_id)
    partition_id = self.start_requested_software_instance.getAggregateValue(
        portal_type='Compute Partition').getReference()
    created_at = rfc1123_date(DateTime())
    since = created_at
    self.login(self.start_requested_software_instance.getUserId())
    response = self.portal_slap.getComputePartitionStatus(self.compute_node_id,
      partition_id)
    self.assertEqual(200, response.status)
    self.assertEqual( 'public, max-age=60, stale-if-error=604800',
        response.headers.get('cache-control'))
    self.assertEqual('REMOTE_USER',
        response.headers.get('vary'))
    self.assertTrue('last-modified' in response.headers)
    self.assertEqual('text/xml; charset=utf-8',
        response.headers.get('content-type'))
    # check returned XML
    xml_fp = StringIO.StringIO()

    xml.dom.ext.PrettyPrint(xml.dom.ext.reader.Sax.FromXml(response.body),
        stream=xml_fp)
    xml_fp.seek(0)
    got_xml = xml_fp.read()
    expected_xml = """\
<?xml version='1.0' encoding='UTF-8'?>
<marshal>
  <dictionary id='i2'>
    <string>created_at</string>
    <string>%(created_at)s</string>
    <string>since</string>
    <string>%(since)s</string>
    <string>state</string>
    <string/>
    <string>text</string>
    <string>#error no data found for %(instance_guid)s</string>
    <string>user</string>
    <string>SlapOS Master</string>
  </dictionary>
</marshal>
""" % dict(
  created_at=created_at,
  since=since,
  instance_guid=self.start_requested_software_instance.getReference(),
)
    self.assertEqual(expected_xml, got_xml,
        '\n'.join([q for q in difflib.unified_diff(expected_xml.split('\n'), got_xml.split('\n'))]))

  def test_getComputePartitionStatus_visited(self):
    self._makeComplexComputeNode(person=self.person)
    self.login(self.person_user_id)
    partition_id = self.start_requested_software_instance.getAggregateValue(
        portal_type='Compute Partition').getReference()
    created_at = rfc1123_date(DateTime())
    since = created_at
    self.login(self.start_requested_software_instance.getUserId())
    self.portal_slap.registerComputePartition(self.compute_node_id, partition_id)
    self.login(self.person_user_id)
    response = self.portal_slap.getComputePartitionStatus(self.compute_node_id,
      partition_id)
    self.assertEqual(200, response.status)
    self.assertEqual( 'public, max-age=60, stale-if-error=604800',
        response.headers.get('cache-control'))
    self.assertEqual('REMOTE_USER',
        response.headers.get('vary'))
    self.assertTrue('last-modified' in response.headers)
    self.assertEqual('text/xml; charset=utf-8',
        response.headers.get('content-type'))
    # check returned XML
    xml_fp = StringIO.StringIO()

    xml.dom.ext.PrettyPrint(xml.dom.ext.reader.Sax.FromXml(response.body),
        stream=xml_fp)
    xml_fp.seek(0)
    got_xml = xml_fp.read()
    expected_xml = """\
<?xml version='1.0' encoding='UTF-8'?>
<marshal>
  <dictionary id='i2'>
    <string>created_at</string>
    <string>%(created_at)s</string>
    <string>since</string>
    <string>%(since)s</string>
    <string>state</string>
    <string/>
    <string>text</string>
    <string>#error no data found for %(instance_guid)s</string>
    <string>user</string>
    <string>SlapOS Master</string>
  </dictionary>
</marshal>
""" % dict(
  created_at=created_at,
  since=since,
  instance_guid=self.start_requested_software_instance.getReference(),
  compute_node_id=self.compute_node_id,
  partition_id=partition_id
)
    self.assertEqual(expected_xml, got_xml,
        '\n'.join([q for q in difflib.unified_diff(expected_xml.split('\n'), got_xml.split('\n'))]))

  def test_registerComputePartition_withSlave(self):
    self._makeComplexComputeNode(person=self.person, with_slave=True)
    partition_id = self.start_requested_software_instance.getAggregateValue(
        portal_type='Compute Partition').getReference()
    self.login(self.person_user_id)
    response = self.portal_slap.registerComputePartition(self.compute_node_id, partition_id)
    self.assertEqual(200, response.status)
    self.assertEqual( 'public, max-age=1, stale-if-error=604800',
        response.headers.get('cache-control'))
    self.assertEqual('REMOTE_USER',
        response.headers.get('vary'))
    self.assertTrue('last-modified' in response.headers)
    self.assertEqual('text/xml; charset=utf-8',
        response.headers.get('content-type'))
    # check returned XML
    xml_fp = StringIO.StringIO()

    xml.dom.ext.PrettyPrint(xml.dom.ext.reader.Sax.FromXml(response.body),
        stream=xml_fp)
    xml_fp.seek(0)
    got_xml = xml_fp.read()
    expected_xml = """\
<?xml version='1.0' encoding='UTF-8'?>
<marshal>
  <object id='i2' module='slapos.slap.slap' class='ComputePartition'>
    <tuple>
      <string>%(compute_node_id)s</string>
      <string>partition1</string>
    </tuple>
    <dictionary id='i3'>
      <string>_compute_node_id</string>
      <string>%(compute_node_id)s</string>
      <string>_connection_dict</string>
      <dictionary id='i4'/>
      <string>_filter_dict</string>
      <dictionary id='i5'>
        <string>paramé</string>
        <string>%(sla)s</string>
      </dictionary>
      <string>_instance_guid</string>
      <string>%(instance_guid)s</string>
      <string>_need_modification</string>
      <int>1</int>
      <string>_parameter_dict</string>
      <dictionary id='i6'>
        <string>full_ip_list</string>
        <list id='i7'/>
        <string>instance_title</string>
        <string>%(instance_title)s</string>
        <string>ip_list</string>
        <list id='i8'>
          <tuple>
            <string/>
            <string>ip_address_1</string>
          </tuple>
        </list>
        <string>paramé</string>
        <string>%(param)s</string>
        <string>root_instance_short_title</string>
        <string/>
        <string>root_instance_title</string>
        <string>%(root_instance_title)s</string>
        <string>slap_compute_node_id</string>
        <string>%(compute_node_id)s</string>
        <string>slap_compute_partition_id</string>
        <string>partition1</string>
        <string>slap_software_release_url</string>
        <string>%(software_release_url)s</string>
        <string>slap_software_type</string>
        <string>%(software_type)s</string>
        <string>slave_instance_list</string>
        <list id='i9'>
          <dictionary id='i10'>
            <string>connection-parameter-hash</string>
            <string>4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945</string>
            <string>paramé</string>
            <string>%(slave_1_param)s</string>
            <string>slap_software_type</string>
            <string>%(slave_1_software_type)s</string>
            <string>slave_reference</string>
            <string>%(slave_1_instance_guid)s</string>
            <string>slave_title</string>
            <string>%(slave_1_title)s</string>
            <string>timestamp</string>
            <int>%(timestamp)s</int>
          </dictionary>
        </list>
        <string>timestamp</string>
        <string>%(timestamp)s</string>
      </dictionary>
      <string>_partition_id</string>
      <string>partition1</string>
      <string>_request_dict</string>
      <none/>
      <string>_requested_state</string>
      <string>started</string>
      <string>_software_release_document</string>
      <object id='i11' module='slapos.slap.slap' class='SoftwareRelease'>
        <tuple>
          <string>%(software_release_url)s</string>
          <string>%(compute_node_id)s</string>
        </tuple>
        <dictionary id='i12'>
          <string>_compute_node_guid</string>
          <string>%(compute_node_id)s</string>
          <string>_software_instance_list</string>
          <list id='i13'/>
          <string>_software_release</string>
          <string>%(software_release_url)s</string>
        </dictionary>
      </object>
      <string>_synced</string>
      <bool>1</bool>
    </dictionary>
  </object>
</marshal>
""" % dict(
  compute_node_id=self.compute_node_id,
  param=self.start_requested_software_instance.getInstanceXmlAsDict()['paramé'],
  sla=self.start_requested_software_instance.getSlaXmlAsDict()['paramé'],
  software_release_url=self.start_requested_software_instance.getUrlString(),
  timestamp=int(self.start_requested_software_instance.getModificationDate()),
  instance_guid=self.start_requested_software_instance.getReference(),
  instance_title=self.start_requested_software_instance.getTitle(),
  root_instance_title=self.start_requested_software_instance.getSpecialiseValue().getTitle(),
  software_type=self.start_requested_software_instance.getSourceReference(),
  slave_1_param=self.start_requested_slave_instance.getInstanceXmlAsDict()['paramé'],
  slave_1_software_type=self.start_requested_slave_instance.getSourceReference(),
  slave_1_instance_guid=self.start_requested_slave_instance.getReference(),
  slave_1_title=self.start_requested_slave_instance.getTitle(),
)
    self.assertEqual(expected_xml, got_xml,
        '\n'.join([q for q in difflib.unified_diff(expected_xml.split('\n'), got_xml.split('\n'))]))

  def test_registerComputePartition(self):
    self._makeComplexComputeNode(person=self.person)
    partition_id = self.start_requested_software_instance.getAggregateValue(
        portal_type='Compute Partition').getReference()
    self.login(self.person_user_id)
    response = self.portal_slap.registerComputePartition(self.compute_node_id, partition_id)
    self.assertEqual(200, response.status)
    self.assertEqual( 'public, max-age=1, stale-if-error=604800',
        response.headers.get('cache-control'))
    self.assertEqual('REMOTE_USER',
        response.headers.get('vary'))
    self.assertTrue('last-modified' in response.headers)
    self.assertEqual('text/xml; charset=utf-8',
        response.headers.get('content-type'))
    # check returned XML
    xml_fp = StringIO.StringIO()

    xml.dom.ext.PrettyPrint(xml.dom.ext.reader.Sax.FromXml(response.body),
        stream=xml_fp)
    xml_fp.seek(0)
    got_xml = xml_fp.read()
    expected_xml = """\
<?xml version='1.0' encoding='UTF-8'?>
<marshal>
  <object id='i2' module='slapos.slap.slap' class='ComputePartition'>
    <tuple>
      <string>%(compute_node_id)s</string>
      <string>partition1</string>
    </tuple>
    <dictionary id='i3'>
      <string>_compute_node_id</string>
      <string>%(compute_node_id)s</string>
      <string>_connection_dict</string>
      <dictionary id='i4'/>
      <string>_filter_dict</string>
      <dictionary id='i5'>
        <string>paramé</string>
        <string>%(sla)s</string>
      </dictionary>
      <string>_instance_guid</string>
      <string>%(instance_guid)s</string>
      <string>_need_modification</string>
      <int>1</int>
      <string>_parameter_dict</string>
      <dictionary id='i6'>
        <string>full_ip_list</string>
        <list id='i7'/>
        <string>instance_title</string>
        <string>%(instance_title)s</string>
        <string>ip_list</string>
        <list id='i8'>
          <tuple>
            <string/>
            <string>ip_address_1</string>
          </tuple>
        </list>
        <string>paramé</string>
        <string>%(param)s</string>
        <string>root_instance_short_title</string>
        <string/>
        <string>root_instance_title</string>
        <string>%(root_instance_title)s</string>
        <string>slap_compute_node_id</string>
        <string>%(compute_node_id)s</string>
        <string>slap_compute_partition_id</string>
        <string>partition1</string>
        <string>slap_software_release_url</string>
        <string>%(software_release_url)s</string>
        <string>slap_software_type</string>
        <string>%(software_type)s</string>
        <string>slave_instance_list</string>
        <list id='i9'/>
        <string>timestamp</string>
        <string>%(timestamp)s</string>
      </dictionary>
      <string>_partition_id</string>
      <string>partition1</string>
      <string>_request_dict</string>
      <none/>
      <string>_requested_state</string>
      <string>started</string>
      <string>_software_release_document</string>
      <object id='i10' module='slapos.slap.slap' class='SoftwareRelease'>
        <tuple>
          <string>%(software_release_url)s</string>
          <string>%(compute_node_id)s</string>
        </tuple>
        <dictionary id='i11'>
          <string>_compute_node_guid</string>
          <string>%(compute_node_id)s</string>
          <string>_software_instance_list</string>
          <list id='i12'/>
          <string>_software_release</string>
          <string>%(software_release_url)s</string>
        </dictionary>
      </object>
      <string>_synced</string>
      <bool>1</bool>
    </dictionary>
  </object>
</marshal>
""" % dict(
  compute_node_id=self.compute_node_id,
  param=self.start_requested_software_instance.getInstanceXmlAsDict()['paramé'],
  sla=self.start_requested_software_instance.getSlaXmlAsDict()['paramé'],
  software_release_url=self.start_requested_software_instance.getUrlString(),
  timestamp=int(self.start_requested_software_instance.getModificationDate()),
  instance_guid=self.start_requested_software_instance.getReference(),
  instance_title=self.start_requested_software_instance.getTitle(),
  root_instance_title=self.start_requested_software_instance.getSpecialiseValue().getTitle(),
  software_type=self.start_requested_software_instance.getSourceReference()
)
    self.assertEqual(expected_xml, got_xml,
        '\n'.join([q for q in difflib.unified_diff(expected_xml.split('\n'), got_xml.split('\n'))]))

  def assertInstanceBangSimulator(self, args, kwargs):
    stored = eval(open(self.instance_bang_simulator).read()) #pylint: disable=eval-used
    # do the same translation magic as in workflow
    kwargs['comment'] = kwargs.pop('comment')
    self.assertEqual(stored,
      [{'recargs': args, 'reckwargs': kwargs,
      'recmethod': 'bang'}])

  def test_softwareInstanceBang(self):
    self._makeComplexComputeNode(person=self.person)
    self.instance_bang_simulator = tempfile.mkstemp()[1]
    try:
      partition_id = self.start_requested_software_instance.getAggregateValue(
          portal_type='Compute Partition').getReference()
      self.login(self.person_user_id)
      self.start_requested_software_instance.bang = Simulator(
        self.instance_bang_simulator, 'bang')
      error_log = 'Please bang me'
      response = self.portal_slap.softwareInstanceBang(self.compute_node_id,
        partition_id, error_log)
      self.assertEqual('OK', response)
      created_at = rfc1123_date(DateTime())
      since = created_at
      response = self.portal_slap.getComputePartitionStatus(self.compute_node_id,
        partition_id)
      # check returned XML
      xml_fp = StringIO.StringIO()

      xml.dom.ext.PrettyPrint(xml.dom.ext.reader.Sax.FromXml(response.body),
          stream=xml_fp)
      xml_fp.seek(0)
      got_xml = xml_fp.read()
      expected_xml = """\
<?xml version='1.0' encoding='UTF-8'?>
<marshal>
  <dictionary id='i2'>
    <string>created_at</string>
    <string>%(created_at)s</string>
    <string>since</string>
    <string>%(since)s</string>
    <string>state</string>
    <string/>
    <string>text</string>
    <string>#error bang called</string>
    <string>user</string>
    <string>%(person_reference)s</string>
  </dictionary>
</marshal>
""" % dict(
    created_at=created_at,
    since=since,
    person_reference=self.person_reference,
  )
      self.assertEqual(expected_xml, got_xml,
          '\n'.join([q for q in difflib.unified_diff(expected_xml.split('\n'), got_xml.split('\n'))]))
      self.assertInstanceBangSimulator((), {'comment': error_log, 'bang_tree': True})
    finally:
      if os.path.exists(self.instance_bang_simulator):
        os.unlink(self.instance_bang_simulator)

  def assertInstanceRenameSimulator(self, args, kwargs):
    stored = eval(open(self.instance_rename_simulator).read()) #pylint: disable=eval-used
    # do the same translation magic as in workflow
    self.assertEqual(stored,
      [{'recargs': args, 'reckwargs': kwargs,
      'recmethod': 'rename'}])

  def test_softwareInstanceRename(self):
    self._makeComplexComputeNode(person=self.person)
    self.instance_rename_simulator = tempfile.mkstemp()[1]
    try:
      partition_id = self.start_requested_software_instance.getAggregateValue(
          portal_type='Compute Partition').getReference()
      self.login(self.person_user_id)
      self.start_requested_software_instance.rename = Simulator(
        self.instance_rename_simulator, 'rename')
      new_name = 'new me'
      response = self.portal_slap.softwareInstanceRename(new_name, self.compute_node_id,
        partition_id)
      self.assertEqual('None', response)
      self.assertInstanceRenameSimulator((), {
          'comment': 'Rename %s into %s' % (self.start_requested_software_instance.getTitle(),
            new_name), 'new_name': new_name})
    finally:
      if os.path.exists(self.instance_rename_simulator):
        os.unlink(self.instance_rename_simulator)

  def assertInstanceRequestSimulator(self, args, kwargs):
    stored = eval(open(self.instance_request_simulator).read()) #pylint: disable=eval-used
    # do the same translation magic as in workflow
    self.assertEqual(stored,
      [{'recargs': args, 'reckwargs': kwargs,
      'recmethod': 'requestSoftwareInstance'}])

  def test_request_withSlave(self):
    self.instance_request_simulator = tempfile.mkstemp()[1]
    try:
      self.login(self.person_user_id)
      self.person.requestSoftwareInstance = Simulator(
        self.instance_request_simulator, 'requestSoftwareInstance')
      response = self.portal_slap.requestComputePartition(
          software_release='req_release',
          software_type='req_type',
          partition_reference='req_reference',
          partition_parameter_xml='<marshal><dictionary id="i2"/></marshal>',
          filter_xml='<marshal><dictionary id="i2"/></marshal>',
          state='<marshal><string>started</string></marshal>',
          shared_xml='<marshal><bool>1</bool></marshal>',
          )
      self.assertEqual(408, response.status)
      self.assertEqual('private',
          response.headers.get('cache-control'))
      self.assertInstanceRequestSimulator((), {
          'instance_xml': "<?xml version='1.0' encoding='utf-8'?>\n<instance/>\n",
          'software_title': 'req_reference',
          'software_release': 'req_release',
          'state': 'started',
          'sla_xml': "<?xml version='1.0' encoding='utf-8'?>\n<instance/>\n",
          'software_type': 'req_type',
          'shared': True})
    finally:
      if os.path.exists(self.instance_request_simulator):
        os.unlink(self.instance_request_simulator)

  def test_request(self):
    self.instance_request_simulator = tempfile.mkstemp()[1]
    try:
      self.login(self.person_user_id)
      self.person.requestSoftwareInstance = Simulator(
        self.instance_request_simulator, 'requestSoftwareInstance')
      response = self.portal_slap.requestComputePartition(
          software_release='req_release',
          software_type='req_type',
          partition_reference='req_reference',
          partition_parameter_xml='<marshal><dictionary id="i2"/></marshal>',
          filter_xml='<marshal><dictionary id="i2"/></marshal>',
          state='<marshal><string>started</string></marshal>',
          shared_xml='<marshal><bool>0</bool></marshal>',
          )
      self.assertEqual(408, response.status)
      self.assertEqual('private',
          response.headers.get('cache-control'))
      self.assertInstanceRequestSimulator((), {
          'instance_xml': "<?xml version='1.0' encoding='utf-8'?>\n<instance/>\n",
          'software_title': 'req_reference',
          'software_release': 'req_release',
          'state': 'started',
          'sla_xml': "<?xml version='1.0' encoding='utf-8'?>\n<instance/>\n",
          'software_type': 'req_type',
          'shared': False})
    finally:
      if os.path.exists(self.instance_request_simulator):
        os.unlink(self.instance_request_simulator)

  def test_request_allocated_instance(self):
    self.tic()
    self.person.edit(
      default_email_coordinate_text="%s@example.org" % self.person.getReference(),
      career_role='member',
    )
    self._makeComplexComputeNode(person=self.person)
    self.start_requested_software_instance.updateLocalRolesOnSecurityGroups()
    self.tic()
    self.login(self.person_user_id)
    response = self.portal_slap.requestComputePartition(
      software_release=self.start_requested_software_instance.getUrlString(),
      software_type=self.start_requested_software_instance.getSourceReference(),
      partition_reference=self.start_requested_software_instance.getTitle(),
      partition_parameter_xml='<marshal><dictionary id="i2"/></marshal>',
      filter_xml='<marshal><dictionary id="i2"/></marshal>',
      state='<marshal><string>started</string></marshal>',
      shared_xml='<marshal><bool>0</bool></marshal>',
      )
    self.assertEqual(type(response), str)
    # check returned XML
    xml_fp = StringIO.StringIO()

    xml.dom.ext.PrettyPrint(xml.dom.ext.reader.Sax.FromXml(response),
        stream=xml_fp)
    xml_fp.seek(0)
    got_xml = xml_fp.read()
    expected_xml = """\
<?xml version='1.0' encoding='UTF-8'?>
<marshal>
  <object id='i2' module='slapos.slap.slap' class='SoftwareInstance'>
    <tuple/>
    <dictionary id='i3'>
      <string>_connection_dict</string>
      <dictionary id='i4'/>
      <string>_filter_dict</string>
      <dictionary id='i5'/>
      <string>_instance_guid</string>
      <string>%(instance_guid)s</string>
      <string>_parameter_dict</string>
      <dictionary id='i6'/>
      <string>_requested_state</string>
      <string>%(state)s</string>
      <string>full_ip_list</string>
      <list id='i7'/>
      <string>instance_title</string>
      <string>%(instance_title)s</string>
      <string>ip_list</string>
      <list id='i8'>
        <tuple>
          <string/>
          <string>%(ip)s</string>
        </tuple>
      </list>
      <string>root_instance_short_title</string>
      <string/>
      <string>root_instance_title</string>
      <string>%(root_instance_title)s</string>
      <string>slap_compute_node_id</string>
      <string>%(compute_node_id)s</string>
      <string>slap_compute_partition_id</string>
      <string>%(partition_id)s</string>
      <string>slap_software_release_url</string>
      <string>%(url_string)s</string>
      <string>slap_software_type</string>
      <string>%(type)s</string>
      <string>slave_instance_list</string>
      <list id='i9'/>
      <string>timestamp</string>
      <string>%(timestamp)s</string>
    </dictionary>
  </object>
</marshal>
""" % dict(
    instance_guid=self.start_requested_software_instance.getReference(),
    instance_title=self.start_requested_software_instance.getTitle(),
    root_instance_title=self.start_requested_software_instance.getSpecialiseTitle(),
    state="started",
    url_string=self.start_requested_software_instance.getUrlString(),
    type=self.start_requested_software_instance.getSourceReference(),
    timestamp=int(self.start_requested_software_instance.getModificationDate()),
    compute_node_id=self.compute_node_id,
    partition_id=self.start_requested_software_instance.getAggregateId(),
    ip=self.start_requested_software_instance.getAggregateValue()\
               .getDefaultNetworkAddressIpAddress(),
  )
    self.assertEqual(expected_xml, got_xml,
      '\n'.join([q for q in difflib.unified_diff(expected_xml.split('\n'),
                                                 got_xml.split('\n'))]))

  def assertSupplySimulator(self, args, kwargs):
    stored = eval(open(self.compute_node_supply_simulator).read()) #pylint: disable=eval-used
    # do the same translation magic as in workflow
    kwargs['software_release_url'] = kwargs.pop('software_release_url')
    kwargs['state'] = kwargs.pop('state')
    self.assertEqual(stored,
      [{'recargs': args, 'reckwargs': kwargs,
      'recmethod': 'requestSoftwareRelease'}])

  def test_compute_nodeSupply(self):
    self.compute_node_supply_simulator = tempfile.mkstemp()[1]
    try:
      self.login(self.person_user_id)
      self.compute_node.requestSoftwareRelease = Simulator(
        self.compute_node_supply_simulator, 'requestSoftwareRelease')
      software_url = 'live_test_url_%s' % self.generateNewId()
      response = self.portal_slap.supplySupply(
          software_url,
          self.compute_node_id,
          state='destroyed')
      self.assertEqual('None', response)
      self.assertSupplySimulator((), {
        'software_release_url': software_url,
        'state': 'destroyed'})
    finally:
      if os.path.exists(self.compute_node_supply_simulator):
        os.unlink(self.compute_node_supply_simulator)

  def assertRequestComputeNodeSimulator(self, args, kwargs):
    stored = eval(open(self.compute_node_request_compute_node_simulator).read()) #pylint: disable=eval-used
    # do the same translation magic as in workflow
    kwargs['compute_node_title'] = kwargs.pop('compute_node_title')
    self.assertEqual(stored,
      [{'recargs': args, 'reckwargs': kwargs,
      'recmethod': 'requestComputeNode'}])

  def test_requestComputeNode(self):
    self.compute_node_request_compute_node_simulator = tempfile.mkstemp()[1]
    try:
      self.login(self.person_user_id)
      self.person.requestComputeNode = Simulator(
        self.compute_node_request_compute_node_simulator, 'requestComputeNode')

      compute_node_id = 'Foo ComputeNode'
      compute_node_reference = 'live_comp_%s' % self.generateNewId()
      self.portal.REQUEST.set('compute_node_reference', compute_node_reference)
      response = self.portal_slap.requestComputeNode(compute_node_id)

      # check returned XML
      xml_fp = StringIO.StringIO()

      xml.dom.ext.PrettyPrint(xml.dom.ext.reader.Sax.FromXml(response),
          stream=xml_fp)
      xml_fp.seek(0)
      got_xml = xml_fp.read()
      expected_xml = """\
<?xml version='1.0' encoding='UTF-8'?>
<marshal>
  <object id='i2' module='slapos.slap.slap' class='Compute Node'>
    <tuple>
      <string>%(compute_node_id)s</string>
    </tuple>
    <dictionary id='i3'>
      <string>_compute_node_id</string>
      <string>%(compute_node_id)s</string>
    </dictionary>
  </object>
</marshal>
""" % {'compute_node_id': compute_node_reference}

      self.assertEqual(expected_xml, got_xml,
          '\n'.join([q for q in difflib.unified_diff(expected_xml.split('\n'), got_xml.split('\n'))]))
      self.assertRequestComputeNodeSimulator((), {'compute_node_title': compute_node_id})
    finally:
      if os.path.exists(self.compute_node_request_compute_node_simulator):
        os.unlink(self.compute_node_request_compute_node_simulator)

  def assertGenerateComputeNodeCertificateSimulator(self, args, kwargs):
    stored = eval(open(self.generate_compute_node_certificate_simulator).read()) #pylint: disable=eval-used
    # do the same translation magic as in workflow
    self.assertEqual(stored,
      [{'recargs': args, 'reckwargs': kwargs,
      'recmethod': 'generateComputeNodeCertificate'}])

  def test_generateComputeNodeCertificate(self):
    self.generate_compute_node_certificate_simulator = tempfile.mkstemp()[1]
    try:
      self.login(self.person_user_id)
      self.compute_node.generateCertificate = Simulator(
        self.generate_compute_node_certificate_simulator, 
        'generateComputeNodeCertificate')

      compute_node_certificate = 'live_\ncertificate_%s' % self.generateNewId()
      compute_node_key = 'live_\nkey_%s' % self.generateNewId()
      self.portal.REQUEST.set('compute_node_certificate', compute_node_certificate)
      self.portal.REQUEST.set('compute_node_key', compute_node_key)
      response = self.portal_slap.generateComputeNodeCertificate(self.compute_node_id)

      # check returned XML
      xml_fp = StringIO.StringIO()

      xml.dom.ext.PrettyPrint(xml.dom.ext.reader.Sax.FromXml(response),
          stream=xml_fp)
      xml_fp.seek(0)
      got_xml = xml_fp.read()
      expected_xml = """\
<?xml version='1.0' encoding='UTF-8'?>
<marshal>
  <dictionary id='i2'>
    <string>certificate</string>
    <string>%(compute_node_certificate)s</string>
    <string>key</string>
    <string>%(compute_node_key)s</string>
  </dictionary>
</marshal>
""" % {'compute_node_key': compute_node_key, 'compute_node_certificate': compute_node_certificate}

      self.assertEqual(expected_xml, got_xml,
          '\n'.join([q for q in difflib.unified_diff(expected_xml.split('\n'), got_xml.split('\n'))]))
      self.assertGenerateComputeNodeCertificateSimulator((), {})
    finally:
      if os.path.exists(self.generate_compute_node_certificate_simulator):
        os.unlink(self.generate_compute_node_certificate_simulator)

  def assertRevokeComputeNodeCertificateSimulator(self, args, kwargs):
    stored = eval(open(self.revoke_compute_node_certificate_simulator).read()) #pylint: disable=eval-used
    # do the same translation magic as in workflow
    self.assertEqual(stored,
      [{'recargs': args, 'reckwargs': kwargs,
      'recmethod': 'revokeComputeNodeCertificate'}])

  def test_revokeComputeNodeCertificate(self):
    self.revoke_compute_node_certificate_simulator = tempfile.mkstemp()[1]
    try:
      self.login(self.person_user_id)
      self.compute_node.revokeCertificate = Simulator(
        self.revoke_compute_node_certificate_simulator,
        'revokeComputeNodeCertificate')

      response = self.portal_slap.revokeComputeNodeCertificate(self.compute_node_id)
      self.assertEqual('None', response)
      self.assertRevokeComputeNodeCertificateSimulator((), {})
    finally:
      if os.path.exists(self.revoke_compute_node_certificate_simulator):
        os.unlink(self.revoke_compute_node_certificate_simulator)

  def test_getHateoasUrl_NotConfigured(self):
    for preference in \
      self.portal.portal_catalog(portal_type="System Preference"):
      preference = preference.getObject()
      if preference.getPreferenceState() == 'global':
        preference.setPreferredHateoasUrl('')
    self.tic()
    self.login(self.person_user_id)
    self.assertRaises(NotFound, self.portal_slap.getHateoasUrl)

  def test_getHateoasUrl(self):
    for preference in \
      self.portal.portal_catalog(portal_type="System Preference"):
      preference = preference.getObject()
      if preference.getPreferenceState() == 'global':
        preference.setPreferredHateoasUrl('foo')
    self.tic()
    self.login(self.person_user_id)
    response = self.portal_slap.getHateoasUrl()
    self.assertEqual(200, response.status)
    self.assertEqual('foo', response.body)
