Commit 00c3e8a5 authored by Łukasz Nowak's avatar Łukasz Nowak

Merge branch 'master' into networkcache_signature

Conflicts:
	slapos/tests/slapgrid.py
parents b175699c 1eddcede
...@@ -3,6 +3,7 @@ develop = . ...@@ -3,6 +3,7 @@ develop = .
parts = parts =
slapos slapos
pyflakes pyflakes
test
find-links = find-links =
http://www.nexedi.org/static/packages/source/slapos.buildout/ http://www.nexedi.org/static/packages/source/slapos.buildout/
...@@ -44,5 +45,10 @@ eggs = ...@@ -44,5 +45,10 @@ eggs =
rstctl rstctl
interpreter = python interpreter = python
[test]
recipe = zc.recipe.testrunner
eggs =
slapos.core
[versions] [versions]
zc.buildout = 1.5.3-dev-SlapOS-005 zc.buildout = 1.5.3-dev-SlapOS-005
...@@ -29,6 +29,13 @@ from AccessControl import ClassSecurityInfo ...@@ -29,6 +29,13 @@ from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions from Products.ERP5Type import Permissions
from Products.ERP5.Document.Item import Item from Products.ERP5.Document.Item import Item
from lxml import etree from lxml import etree
import collections
class DisconnectedSoftwareTree(Exception):
pass
class CyclicSoftwareTree(Exception):
pass
class SoftwareInstance(Item): class SoftwareInstance(Item):
""" """
...@@ -60,3 +67,44 @@ class SoftwareInstance(Item): ...@@ -60,3 +67,44 @@ class SoftwareInstance(Item):
value = element.text value = element.text
result_dict[key] = value result_dict[key] = value
return result_dict return result_dict
security.declareProtected(Permissions.AccessContentsInformation,
'checkNotCyclic')
def checkNotCyclic(self, graph):
# see http://neopythonic.blogspot.com/2009/01/detecting-cycles-in-directed-graph.html
todo = set(graph.keys())
while todo:
node = todo.pop()
stack = [node]
while stack:
top = stack[-1]
for node in graph[top]:
if node in stack:
raise CyclicSoftwareTree
if node in todo:
stack.append(node)
todo.remove(node)
break
else:
node = stack.pop()
return True
security.declareProtected(Permissions.AccessContentsInformation,
'checkConnected')
def checkConnected(self, graph, root):
size = len(graph)
visited = set()
to_crawl = collections.deque(graph[root])
while to_crawl:
current = to_crawl.popleft()
if current in visited:
continue
visited.add(current)
node_children = set(graph[current])
to_crawl.extend(node_children - visited)
# add one to visited, as root won't be visited, only children
# this is false positive in case of cyclic graphs, but they are
# anyway wrong in Software Instance trees
if size != len(visited) + 1:
raise DisconnectedSoftwareTree
return True
...@@ -64,9 +64,18 @@ instance_xml = kwargs["instance_xml"]\n ...@@ -64,9 +64,18 @@ instance_xml = kwargs["instance_xml"]\n
sla_xml = kwargs["sla_xml"]\n sla_xml = kwargs["sla_xml"]\n
state = kwargs["state"]\n state = kwargs["state"]\n
\n \n
# Get root software instance\n # graph allows to "simulate" tree change after requested operation\n
graph = {}\n
# Get root software instance and create initial graph\n
predecessor_software_instance = software_instance\n predecessor_software_instance = software_instance\n
while (predecessor_software_instance is not None):\n while (predecessor_software_instance is not None):\n
predecessor_software_instance_pred_uid_list = predecessor_software_instance.getPredecessorUidList()\n
graph[predecessor_software_instance.getUid()] = predecessor_software_instance_pred_uid_list\n
# as this walking is not fetching all instances fill predecessors into graph, in order to have\n
# "nearly" complete representation\n
for uid in predecessor_software_instance_pred_uid_list:\n
if uid not in graph:\n
graph[uid] = []\n
root_software_instance = predecessor_software_instance\n root_software_instance = predecessor_software_instance\n
predecessor_software_instance = predecessor_software_instance.getPredecessorRelatedValue(\n predecessor_software_instance = predecessor_software_instance.getPredecessorRelatedValue(\n
portal_type="Software Instance")\n portal_type="Software Instance")\n
...@@ -84,6 +93,11 @@ request_software_instance = software_instance.portal_catalog.getResultValue(\n ...@@ -84,6 +93,11 @@ request_software_instance = software_instance.portal_catalog.getResultValue(\n
root_uid=root_software_instance.getUid(),\n root_uid=root_software_instance.getUid(),\n
)\n )\n
\n \n
# above query does not find root software instance, but as this case is easy\n
# to find, just check if such request does not come and raise\n
if root_software_instance.getTitle() == requested_partition_reference:\n
raise ValueError(\'It is disallowed to request root software instance\')\n
\n
if (request_software_instance is None):\n if (request_software_instance is None):\n
if (portal.portal_activities.countMessageWithTag(tag) > 0):\n if (portal.portal_activities.countMessageWithTag(tag) > 0):\n
# The software instance is already under creation but can not be fetched from catalog\n # The software instance is already under creation but can not be fetched from catalog\n
...@@ -130,6 +144,8 @@ else:\n ...@@ -130,6 +144,8 @@ else:\n
predecessor_software_instance.edit(\n predecessor_software_instance.edit(\n
predecessor_uid_list=predecessor_uid_list,\n predecessor_uid_list=predecessor_uid_list,\n
activate_kw={\'tag\': tag},)\n activate_kw={\'tag\': tag},)\n
graph[predecessor_software_instance.getUid()] = predecessor_uid_list\n
\n
if state == \'started\':\n if state == \'started\':\n
request_software_instance.startRequested()\n request_software_instance.startRequested()\n
request_software_instance.activate(after_tag=tag).requestStartComputerPartition()\n request_software_instance.activate(after_tag=tag).requestStartComputerPartition()\n
...@@ -139,7 +155,16 @@ else:\n ...@@ -139,7 +155,16 @@ else:\n
else:\n else:\n
raise ValueError(\'State %r is not supported\' % state)\n raise ValueError(\'State %r is not supported\' % state)\n
predecessor_list = software_instance.getPredecessorList() + [request_software_instance.getRelativeUrl()]\n predecessor_list = software_instance.getPredecessorList() + [request_software_instance.getRelativeUrl()]\n
# Add requested software instance to graph if does not exists there yet\n
if not request_software_instance.getUid() in graph:\n
graph[request_software_instance.getUid()] = []\n
\n
# update graph to reflect requested operation\n
graph[software_instance.getUid()] = software_instance.getPredecessorUidList() + [request_software_instance.getUid()]\n
\n \n
# check if all elements are still connected and if there is no cycle\n
software_instance.checkConnected(graph, root_software_instance.getUid())\n
software_instance.checkNotCyclic(graph)\n
\n \n
software_instance.edit(\n software_instance.edit(\n
predecessor_list=predecessor_list,\n predecessor_list=predecessor_list,\n
......
246 254
\ No newline at end of file \ No newline at end of file
...@@ -361,6 +361,7 @@ class TestVifibSlapWebService(testVifibMixin): ...@@ -361,6 +361,7 @@ class TestVifibSlapWebService(testVifibMixin):
self.assertEqual(1, len(software_instance_list)) self.assertEqual(1, len(software_instance_list))
software_instance = software_instance_list[0] software_instance = software_instance_list[0]
sequence.edit( sequence.edit(
root_software_instance_title=software_title,
software_instance_uid=software_instance.getUid(), software_instance_uid=software_instance.getUid(),
software_instance_reference=software_instance.getReference(), software_instance_reference=software_instance.getReference(),
hosting_subscription_uid=software_instance.getAggregateRelatedValue( hosting_subscription_uid=software_instance.getAggregateRelatedValue(
...@@ -1187,6 +1188,16 @@ class TestVifibSlapWebService(testVifibMixin): ...@@ -1187,6 +1188,16 @@ class TestVifibSlapWebService(testVifibMixin):
def stepSelectRequestedReferenceChildrenBChild(self, sequence, **kw): def stepSelectRequestedReferenceChildrenBChild(self, sequence, **kw):
sequence.edit(requested_reference='children_b_child') sequence.edit(requested_reference='children_b_child')
def stepSelectRequestedReferenceRootSoftwareInstanceTitle(self, sequence,
**kw):
sequence.edit(requested_reference=sequence['root_software_instance_title'])
def stepSelectRequestedReferenceB(self, sequence, **kw):
sequence.edit(requested_reference='b')
def stepSelectRequestedReferenceC(self, sequence, **kw):
sequence.edit(requested_reference='c')
def stepSelectEmptyRequestedParameterDict(self, sequence, **kw): def stepSelectEmptyRequestedParameterDict(self, sequence, **kw):
sequence.edit(requested_parameter_dict=None) sequence.edit(requested_parameter_dict=None)
...@@ -1355,6 +1366,7 @@ class TestVifibSlapWebService(testVifibMixin): ...@@ -1355,6 +1366,7 @@ class TestVifibSlapWebService(testVifibMixin):
requested_computer_partition_reference=\ requested_computer_partition_reference=\
requested_slap_computer_partition.getId()) requested_slap_computer_partition.getId())
def stepDirectRequestComputerPartitionNotReadyResponseWithoutState(self, def stepDirectRequestComputerPartitionNotReadyResponseWithoutState(self,
sequence, **kw): sequence, **kw):
request_dict = { 'computer_id': sequence['computer_reference'] , request_dict = { 'computer_id': sequence['computer_reference'] ,
...@@ -3103,6 +3115,14 @@ class TestVifibSlapWebService(testVifibMixin): ...@@ -3103,6 +3115,14 @@ class TestVifibSlapWebService(testVifibMixin):
def stepCheckSoftwareInstanceAndRelatedComputerPartition(self, def stepCheckSoftwareInstanceAndRelatedComputerPartition(self,
sequence, **kw): sequence, **kw):
self.stepCheckSoftwareInstanceAndRelatedComputerPartitionNoPackingListCheck(sequence, **kw)
software_instance_uid = sequence['software_instance_uid']
software_instance = self.portal.portal_catalog.getResultValue(
uid=software_instance_uid)
self._checkSoftwareInstanceAndRelatedPartition(software_instance)
def stepCheckSoftwareInstanceAndRelatedComputerPartitionNoPackingListCheck(self,
sequence, **kw):
software_instance_uid = sequence['software_instance_uid'] software_instance_uid = sequence['software_instance_uid']
software_instance = self.portal.portal_catalog.getResultValue( software_instance = self.portal.portal_catalog.getResultValue(
uid=software_instance_uid) uid=software_instance_uid)
...@@ -3110,7 +3130,6 @@ class TestVifibSlapWebService(testVifibMixin): ...@@ -3110,7 +3130,6 @@ class TestVifibSlapWebService(testVifibMixin):
predecessor_value_list = software_instance.getPredecessorValueList() predecessor_value_list = software_instance.getPredecessorValueList()
self.assertEqual(1, len(predecessor_value_list)) self.assertEqual(1, len(predecessor_value_list))
self._checkSoftwareInstanceAndRelatedPartition(software_instance)
sequence.edit( sequence.edit(
requested_software_instance_uid=predecessor_value_list[0].getUid(), requested_software_instance_uid=predecessor_value_list[0].getUid(),
requested_software_instance_reference=predecessor_value_list[0].getReference()) requested_software_instance_reference=predecessor_value_list[0].getReference())
...@@ -7623,12 +7642,42 @@ class TestVifibSlapWebService(testVifibMixin): ...@@ -7623,12 +7642,42 @@ class TestVifibSlapWebService(testVifibMixin):
sequence_list.addSequenceString(sequence_string) sequence_list.addSequenceString(sequence_string)
sequence_list.play(self) sequence_list.play(self)
def stepStoreCurrentSoftwareInstanceUidBufferA(self, sequence, **kw):
sequence['buffer_a_software_instance_uid'] = sequence['software_instance_uid']
def stepStoreCurrentSoftwareInstanceUidBufferB(self, sequence, **kw):
sequence['buffer_b_software_instance_uid'] = sequence['software_instance_uid']
def stepStoreCurrentComputerUidBufferA(self, sequence, **kw):
sequence['buffer_a_computer_uid'] = sequence['computer_uid']
def stepStoreCurrentComputerUidBufferB(self, sequence, **kw):
sequence['buffer_b_computer_uid'] = sequence['computer_uid']
def stepRestoreSoftwareInstanceUidFromBufferA(self, sequence, **kw):
sequence['software_instance_uid'] = sequence['buffer_a_software_instance_uid']
def stepRestoreSoftwareInstanceUidFromBufferB(self, sequence, **kw):
sequence['software_instance_uid'] = sequence['buffer_b_software_instance_uid']
def stepRestoreComputerUidFromBufferA(self, sequence, **kw):
sequence['computer_uid'] = sequence['buffer_a_computer_uid']
def stepRestoreComputerUidFromBufferB(self, sequence, **kw):
sequence['computer_uid'] = sequence['buffer_b_computer_uid']
def stepStoreCurrentComputerReferenceBufferA(self, sequence, **kw): def stepStoreCurrentComputerReferenceBufferA(self, sequence, **kw):
sequence['buffer_a_computer_reference'] = sequence['computer_reference'] sequence['buffer_a_computer_reference'] = sequence['computer_reference']
def stepStoreCurrentComputerReferenceBufferB(self, sequence, **kw): def stepStoreCurrentComputerReferenceBufferB(self, sequence, **kw):
sequence['buffer_b_computer_reference'] = sequence['computer_reference'] sequence['buffer_b_computer_reference'] = sequence['computer_reference']
def stepStoreCurrentComputerPartitionUidBufferA(self, sequence, **kw):
sequence['buffer_a_computer_partition_uid'] = sequence['computer_partition_uid']
def stepStoreCurrentComputerPartitionUidBufferB(self, sequence, **kw):
sequence['buffer_b_computer_partition_uid'] = sequence['computer_partition_uid']
def stepStoreCurrentComputerPartitionReferenceBufferA(self, sequence, **kw): def stepStoreCurrentComputerPartitionReferenceBufferA(self, sequence, **kw):
sequence['buffer_a_computer_partition_reference'] = sequence['computer_partition_reference'] sequence['buffer_a_computer_partition_reference'] = sequence['computer_partition_reference']
...@@ -7641,6 +7690,12 @@ class TestVifibSlapWebService(testVifibMixin): ...@@ -7641,6 +7690,12 @@ class TestVifibSlapWebService(testVifibMixin):
def stepRestoreComputerReferenceFromBufferB(self, sequence, **kw): def stepRestoreComputerReferenceFromBufferB(self, sequence, **kw):
sequence['computer_reference'] = sequence['buffer_b_computer_reference'] sequence['computer_reference'] = sequence['buffer_b_computer_reference']
def stepRestoreComputerPartitionUidFromBufferA(self, sequence, **kw):
sequence['computer_partition_uid'] = sequence['buffer_a_computer_partition_uid']
def stepRestoreComputerPartitionUidFromBufferB(self, sequence, **kw):
sequence['computer_partition_uid'] = sequence['buffer_b_computer_partition_uid']
def stepRestoreComputerPartitionReferenceFromBufferA(self, sequence, **kw): def stepRestoreComputerPartitionReferenceFromBufferA(self, sequence, **kw):
sequence['computer_partition_reference'] = sequence['buffer_a_computer_partition_reference'] sequence['computer_partition_reference'] = sequence['buffer_a_computer_partition_reference']
...@@ -7652,31 +7707,149 @@ class TestVifibSlapWebService(testVifibMixin): ...@@ -7652,31 +7707,149 @@ class TestVifibSlapWebService(testVifibMixin):
If software instance originated on computer comes from another computer it If software instance originated on computer comes from another computer it
shall be possible to sucesfully destroy it. shall be possible to sucesfully destroy it.
Test is done in a way to trigger unstable Assignor role calculation
on Hosting Subscription which leads to unavailability of Software Instances
from one computer to another.
""" """
sequence_list = SequenceList() sequence_list = SequenceList()
sequence_string = self.prepare_install_requested_computer_partition_sequence_string + \ sequence_string = """
""" # Prepare software release shared by both Computers
LoginTestVifibDeveloper
SelectNewSoftwareReleaseUri
CreateSoftwareRelease
Tic
SubmitSoftwareRelease
Tic
CreateSoftwareProduct
Tic
ValidateSoftwareProduct
Tic
SetSoftwareProductToSoftwareRelease
PublishByActionSoftwareRelease
Logout
# Create first computer
LoginTestVifibAdmin
CreateComputer
Tic
Logout
SlapLoginCurrentComputer
FormatComputer
Tic
SlapLogout
StoreCurrentComputerReferenceBufferA StoreCurrentComputerReferenceBufferA
StoreCurrentComputerUidBufferA
# Install software on first computer
LoginTestVifibAdmin
RequestSoftwareInstallation
Tic
Logout
SlapLoginCurrentComputer
ComputerSoftwareReleaseAvailable
Tic
SlapLogout
# Now request and instantiate this software release on first computer
LoginTestVifibCustomer
PersonRequestSoftwareInstance
Tic
Logout
LoginDefaultUser
ConfirmOrderedSaleOrderActiveSense
Tic
SetSelectedComputerPartition
SelectCurrentlyUsedSalePackingListUid
Logout
StoreCurrentComputerPartitionReferenceBufferA StoreCurrentComputerPartitionReferenceBufferA
""" + \ StoreCurrentComputerPartitionUidBufferA
self.prepare_formated_computer + \ StoreCurrentSoftwareInstanceUidBufferA
"""
LoginDefaultUser
CheckComputerPartitionInstanceSetupSalePackingListConfirmed
Logout
# Start it..
SlapLoginCurrentComputer
SoftwareInstanceAvailable
Tic
SlapLogout
LoginDefaultUser
SetSelectedComputerPartition
CheckComputerPartitionInstanceSetupSalePackingListStopped
CheckComputerPartitionInstanceHostingSalePackingListConfirmed
Logout
SlapLoginCurrentComputer
SoftwareInstanceStarted
Tic
SlapLogout
LoginDefaultUser
CheckComputerPartitionInstanceHostingSalePackingListStarted
Logout
# ...stop it...
LoginDefaultUser
RequestSoftwareInstanceStop
Tic
Logout
LoginDefaultUser
CheckComputerPartitionInstanceHostingSalePackingListStopped
Logout
SlapLoginCurrentComputer
SoftwareInstanceStopped
Tic
SlapLogout
LoginDefaultUser
CheckComputerPartitionInstanceHostingSalePackingListDelivered
Logout
# ...and request destruction
LoginDefaultUser
RequestSoftwareInstanceDestroy
Tic
Logout
LoginDefaultUser
CheckComputerPartitionInstanceCleanupSalePackingListConfirmed
Logout
# Now prepare second computer
LoginTestVifibAdmin
CreateComputer
Tic
Logout
SlapLoginCurrentComputer
FormatComputer
Tic
SlapLogout
StoreCurrentComputerReferenceBufferB StoreCurrentComputerReferenceBufferB
StoreCurrentComputerPartitionReferenceBufferB StoreCurrentComputerUidBufferB
LoginTestVifibAdmin LoginTestVifibAdmin
RequestSoftwareInstallation RequestSoftwareInstallation
Tic Tic
Logout Logout
SlapLoginCurrentComputer SlapLoginCurrentComputer
ComputerSoftwareReleaseAvailable ComputerSoftwareReleaseAvailable
Tic Tic
SlapLogout SlapLogout
StoreCurrentComputerReferenceBufferB
StoreCurrentComputerUidBufferB
# Now request self software release from one computer to another
RestoreComputerReferenceFromBufferA RestoreComputerReferenceFromBufferA
RestoreComputerPartitionReferenceFromBufferA RestoreComputerUidFromBufferA
RestoreSoftwareInstanceUidFromBufferA
SlapLoginCurrentSoftwareInstance SlapLoginCurrentSoftwareInstance
RequestComputerPartitionNotReadyResponse RequestComputerPartitionNotReadyResponse
Tic Tic
...@@ -7688,55 +7861,72 @@ class TestVifibSlapWebService(testVifibMixin): ...@@ -7688,55 +7861,72 @@ class TestVifibSlapWebService(testVifibMixin):
SlapLogout SlapLogout
LoginDefaultUser LoginDefaultUser
CheckSoftwareInstanceAndRelatedComputerPartition CheckSoftwareInstanceAndRelatedComputerPartitionNoPackingListCheck
CheckRequestedSoftwareInstanceAndRelatedComputerPartition CheckRequestedSoftwareInstanceAndRelatedComputerPartition
Logout Logout
SlapLoginCurrentSoftwareInstance
CheckRequestedComputerPartitionCleanParameterList
Logout
LoginDefaultUser LoginDefaultUser
SetCurrentSoftwareInstanceRequested SetCurrentSoftwareInstanceRequested
SetSelectedComputerPartition SetSelectedComputerPartition
SelectCurrentlyUsedSalePackingListUid SelectCurrentlyUsedSalePackingListUid
Logout Logout
SlapLoginCurrentSoftwareInstance
CheckRequestedComputerPartitionCleanParameterList
Logout
StoreCurrentComputerPartitionReferenceBufferB
StoreCurrentComputerPartitionUidBufferB
StoreCurrentSoftwareInstanceUidBufferB
RestoreComputerReferenceFromBufferB RestoreComputerReferenceFromBufferB
RestoreComputerPartitionReferenceFromBufferB RestoreComputerUidFromBufferB
# Start the requested software instance...
SlapLoginCurrentComputer SlapLoginCurrentComputer
SoftwareInstanceBuilding SoftwareInstanceAvailable
Tic Tic
SlapLogout SlapLogout
LoginDefaultUser LoginDefaultUser
CheckComputerPartitionInstanceSetupSalePackingListStarted SetSelectedComputerPartition
CheckComputerPartitionInstanceSetupSalePackingListStopped
CheckComputerPartitionInstanceHostingSalePackingListConfirmed
Logout Logout
SlapLoginCurrentComputer SlapLoginCurrentComputer
SoftwareInstanceAvailable SoftwareInstanceStarted
Tic Tic
SlapLogout SlapLogout
LoginDefaultUser LoginDefaultUser
CheckComputerPartitionInstanceSetupSalePackingListStopped CheckComputerPartitionInstanceHostingSalePackingListStarted
CheckComputerPartitionInstanceHostingSalePackingListConfirmed Logout
# ...and stop it
LoginDefaultUser
RequestSoftwareInstanceStop
Tic
Logout
LoginDefaultUser
CheckComputerPartitionInstanceHostingSalePackingListStopped
Logout Logout
SlapLoginCurrentComputer SlapLoginCurrentComputer
SoftwareInstanceStarted SoftwareInstanceStopped
Tic Tic
SlapLogout SlapLogout
LoginDefaultUser LoginDefaultUser
CheckComputerPartitionInstanceHostingSalePackingListStarted CheckComputerPartitionInstanceHostingSalePackingListDelivered
SetCurrentSoftwareInstanceRequester
SetSelectedComputerPartition
SelectCurrentlyUsedSalePackingListUid
Logout Logout
LoginTestVifibCustomer # Now request destruction of second software instance...
LoginDefaultUser
RequestSoftwareInstanceDestroy RequestSoftwareInstanceDestroy
Tic Tic
Logout Logout
...@@ -7745,8 +7935,24 @@ class TestVifibSlapWebService(testVifibMixin): ...@@ -7745,8 +7935,24 @@ class TestVifibSlapWebService(testVifibMixin):
CheckComputerPartitionInstanceCleanupSalePackingListConfirmed CheckComputerPartitionInstanceCleanupSalePackingListConfirmed
Logout Logout
RestoreComputerReferenceFromBufferA # ...and destroy it
SlapLoginCurrentComputer
SoftwareInstanceDestroyed
Tic
SlapLogout
LoginDefaultUser
CheckComputerPartitionInstanceCleanupSalePackingListDelivered
CheckComputerPartitionIsFree
Logout
# Time to switch back to first software instance and destroy it
RestoreComputerPartitionReferenceFromBufferA RestoreComputerPartitionReferenceFromBufferA
RestoreComputerPartitionUidFromBufferA
RestoreSoftwareInstanceUidFromBufferA
RestoreComputerReferenceFromBufferA
RestoreComputerUidFromBufferA
SlapLoginCurrentComputer SlapLoginCurrentComputer
SoftwareInstanceDestroyed SoftwareInstanceDestroyed
...@@ -7760,15 +7966,741 @@ class TestVifibSlapWebService(testVifibMixin): ...@@ -7760,15 +7966,741 @@ class TestVifibSlapWebService(testVifibMixin):
""" """
sequence_list.addSequenceString(sequence_string) sequence_list.addSequenceString(sequence_string)
sequence_list.play(self) sequence_list.play(self)
def test_bug_hosting_subscription_assignor_role_instability(self):
"""Show instability issue of Assignor role on Hosting Subscription
Related to fact when Hosting Subscription is associated to
Software Instances deployed on many computers"""
raise NotImplementedError raise NotImplementedError
def test_bug_destruction_with_unfinished_packing_list(self):
"""Proves that even if some packing lists are not fully delivered
it is possible to destroy software instance"""
sequence_list = SequenceList()
sequence_string = """
# Prepare software release
LoginTestVifibDeveloper
SelectNewSoftwareReleaseUri
CreateSoftwareRelease
Tic
SubmitSoftwareRelease
Tic
CreateSoftwareProduct
Tic
ValidateSoftwareProduct
Tic
SetSoftwareProductToSoftwareRelease
PublishByActionSoftwareRelease
Logout
# Create first computer
LoginTestVifibAdmin
CreateComputer
Tic
Logout
SlapLoginCurrentComputer
FormatComputer
Tic
SlapLogout
StoreCurrentComputerReferenceBufferA
StoreCurrentComputerUidBufferA
# Install software on first computer
LoginTestVifibAdmin
RequestSoftwareInstallation
Tic
Logout
SlapLoginCurrentComputer
ComputerSoftwareReleaseAvailable
Tic
SlapLogout
# Now request and instantiate this software release on first computer
LoginTestVifibCustomer
PersonRequestSoftwareInstance
Tic
Logout
LoginDefaultUser
ConfirmOrderedSaleOrderActiveSense
Tic
SetSelectedComputerPartition
SelectCurrentlyUsedSalePackingListUid
Logout
LoginDefaultUser
CheckComputerPartitionInstanceSetupSalePackingListConfirmed
Logout
# Request destruction...
LoginDefaultUser
RequestSoftwareInstanceDestroy
Tic
Logout
LoginDefaultUser
CheckComputerPartitionInstanceCleanupSalePackingListConfirmed
Logout
# ...and destroy it
SlapLoginCurrentComputer
SoftwareInstanceDestroyed
Tic
SlapLogout
LoginDefaultUser
CheckComputerPartitionInstanceCleanupSalePackingListDelivered
CheckComputerPartitionIsFree
Logout
"""
sequence_list.addSequenceString(sequence_string)
sequence_list.play(self)
def stepDirectRequestComputerPartitionRaisesDisconnectedSoftwareTree(self,
sequence, **kw):
software_instance = self.portal.portal_catalog.getResultValue(
uid = sequence['software_instance_uid'])
from erp5.document.SoftwareInstance import DisconnectedSoftwareTree
self.assertRaises(DisconnectedSoftwareTree,
software_instance.requestSoftwareInstance,
software_release=sequence['software_release_uri'],
software_type=sequence['requested_reference'],
partition_reference=sequence['requested_reference'],
shared=False,
instance_xml=self.minimal_correct_xml,
sla_xml=self.minimal_correct_xml,
state='started'
)
def test_bug_orhpaned_software_instance(self):
"""Check that no orphaned Software Instances would be created
In below scenario system shall behave like mentioned:
OpenOrder.request(SR, A) | SR(A)
A.request(SR, B) | SR(A) <- SR(B)
B.request(SR, C) | SR(A) <- SR(B) <- SR(C)
C.request(SR, B) raises immediately, because the result would be:
SR(A)
SR(B) <- SR(C)
do B would become root of orphaned tree.
"""
# Setup sufficient amount of CP
self.computer_partition_amount = 3
sequence_list = SequenceList()
sequence_string = """
# Prepare software release
LoginTestVifibDeveloper
SelectNewSoftwareReleaseUri
CreateSoftwareRelease
Tic
SubmitSoftwareRelease
Tic
CreateSoftwareProduct
Tic
ValidateSoftwareProduct
Tic
SetSoftwareProductToSoftwareRelease
PublishByActionSoftwareRelease
Logout
# Create the computer
LoginTestVifibAdmin
CreateComputer
Tic
Logout
SlapLoginCurrentComputer
FormatComputer
Tic
SlapLogout
StoreCurrentComputerReferenceBufferA
StoreCurrentComputerUidBufferA
# Install the software release
LoginTestVifibAdmin
RequestSoftwareInstallation
Tic
Logout
SlapLoginCurrentComputer
ComputerSoftwareReleaseAvailable
Tic
SlapLogout
# Create Software Instance A (originates from Open Order)
LoginTestVifibCustomer
PersonRequestSoftwareInstance
Tic
Logout
LoginDefaultUser
ConfirmOrderedSaleOrderActiveSense
Tic
SetSelectedComputerPartition
SelectCurrentlyUsedSalePackingListUid
Logout
LoginDefaultUser
CheckComputerPartitionInstanceSetupSalePackingListConfirmed
Logout
# From root request B
SelectRequestedReferenceB
SlapLoginCurrentSoftwareInstance
RequestComputerPartitionNotReadyResponse
Tic
SlapLogout
SlapLoginCurrentSoftwareInstance
RequestComputerPartition
Tic
SlapLogout
LoginDefaultUser
CheckSoftwareInstanceAndRelatedComputerPartitionNoPackingListCheck
CheckRequestedSoftwareInstanceAndRelatedComputerPartition
Logout
LoginDefaultUser
SetCurrentSoftwareInstanceRequested
SetSelectedComputerPartition
SelectCurrentlyUsedSalePackingListUid
Logout
SlapLoginCurrentSoftwareInstance
CheckRequestedComputerPartitionCleanParameterList
Logout
# From B request C
SelectRequestedReferenceC
SlapLoginCurrentSoftwareInstance
RequestComputerPartitionNotReadyResponse
Tic
SlapLogout
SlapLoginCurrentSoftwareInstance
RequestComputerPartition
Tic
SlapLogout
LoginDefaultUser
CheckSoftwareInstanceAndRelatedComputerPartitionNoPackingListCheck
CheckRequestedSoftwareInstanceAndRelatedComputerPartition
Logout
LoginDefaultUser
SetCurrentSoftwareInstanceRequested
SetSelectedComputerPartition
SelectCurrentlyUsedSalePackingListUid
Logout
SlapLoginCurrentSoftwareInstance
CheckRequestedComputerPartitionCleanParameterList
Logout
# Try to: from C request B and prove that it raises
SelectRequestedReferenceB
LoginDefaultUser # login as superuser in order to work in erp5
DirectRequestComputerPartitionRaisesDisconnectedSoftwareTree
"""
sequence_list.addSequenceString(sequence_string)
sequence_list.play(self)
def stepDirectRequestComputerPartitionRaisesCyclicSoftwareTree(self,
sequence, **kw):
software_instance = self.portal.portal_catalog.getResultValue(
uid = sequence['software_instance_uid'])
from erp5.document.SoftwareInstance import CyclicSoftwareTree
self.assertRaises(CyclicSoftwareTree,
software_instance.requestSoftwareInstance,
software_release=sequence['software_release_uri'],
software_type=sequence['requested_reference'],
partition_reference=sequence['requested_reference'],
shared=False,
instance_xml=self.minimal_correct_xml,
sla_xml=self.minimal_correct_xml,
state='started'
)
def test_bug_cyclic_software_instance(self):
"""Check that no cyclic Software Instance trees would be created
In below scenario system shall behave like mentioned:
OpenOrder.request(SR, A) | SR(A)
A.request(SR, B) | SR(A) <- SR(B)
B.request(SR, A) | SR(A) <- SR(B) <- SR(C)
C.request(SR, B) raises immediately, because the result would be:
SR(A)
SR(B) <-> SR(C)
so B and C would be cyclic
"""
# Setup sufficient amount of CP
self.computer_partition_amount = 3
sequence_list = SequenceList()
sequence_string = """
# Prepare software release
LoginTestVifibDeveloper
SelectNewSoftwareReleaseUri
CreateSoftwareRelease
Tic
SubmitSoftwareRelease
Tic
CreateSoftwareProduct
Tic
ValidateSoftwareProduct
Tic
SetSoftwareProductToSoftwareRelease
PublishByActionSoftwareRelease
Logout
# Create the computer
LoginTestVifibAdmin
CreateComputer
Tic
Logout
SlapLoginCurrentComputer
FormatComputer
Tic
SlapLogout
StoreCurrentComputerReferenceBufferA
StoreCurrentComputerUidBufferA
# Install the software release
LoginTestVifibAdmin
RequestSoftwareInstallation
Tic
Logout
SlapLoginCurrentComputer
ComputerSoftwareReleaseAvailable
Tic
SlapLogout
# Create Software Instance A (originates from Open Order)
LoginTestVifibCustomer
PersonRequestSoftwareInstance
Tic
Logout
LoginDefaultUser
ConfirmOrderedSaleOrderActiveSense
Tic
SetSelectedComputerPartition
SelectCurrentlyUsedSalePackingListUid
Logout
LoginDefaultUser
CheckComputerPartitionInstanceSetupSalePackingListConfirmed
Logout
# From root request B
SelectRequestedReferenceB
SlapLoginCurrentSoftwareInstance
RequestComputerPartitionNotReadyResponse
Tic
SlapLogout
SlapLoginCurrentSoftwareInstance
RequestComputerPartition
Tic
SlapLogout
LoginDefaultUser
CheckSoftwareInstanceAndRelatedComputerPartitionNoPackingListCheck
CheckRequestedSoftwareInstanceAndRelatedComputerPartition
Logout
LoginDefaultUser
SetCurrentSoftwareInstanceRequested
SetSelectedComputerPartition
SelectCurrentlyUsedSalePackingListUid
Logout
SlapLoginCurrentSoftwareInstance
CheckRequestedComputerPartitionCleanParameterList
Logout
# From B request C
SelectRequestedReferenceC
SlapLoginCurrentSoftwareInstance
RequestComputerPartitionNotReadyResponse
Tic
SlapLogout
SlapLoginCurrentSoftwareInstance
RequestComputerPartition
Tic
SlapLogout
LoginDefaultUser
CheckSoftwareInstanceAndRelatedComputerPartitionNoPackingListCheck
CheckRequestedSoftwareInstanceAndRelatedComputerPartition
Logout
LoginDefaultUser
SetCurrentSoftwareInstanceRequested
SetSelectedComputerPartition
SelectCurrentlyUsedSalePackingListUid
Logout
SlapLoginCurrentSoftwareInstance
CheckRequestedComputerPartitionCleanParameterList
Logout
# Try to: from C request B and prove that it raises
SelectRequestedReferenceB
LoginDefaultUser # login as superuser in order to work in erp5
DirectRequestComputerPartitionRaisesCyclicSoftwareTree
"""
sequence_list.addSequenceString(sequence_string)
sequence_list.play(self)
def stepDirectRequestComputerPartitionRaisesValueError(self,
sequence, **kw):
software_instance = self.portal.portal_catalog.getResultValue(
uid = sequence['software_instance_uid'])
self.assertRaises(ValueError,
software_instance.requestSoftwareInstance,
software_release=sequence['software_release_uri'],
software_type=sequence['requested_reference'],
partition_reference=sequence['requested_reference'],
shared=False,
instance_xml=self.minimal_correct_xml,
sla_xml=self.minimal_correct_xml,
state='started'
)
def test_bug_cyclic_software_instance_small_tree(self):
"""Check that no cyclic Software Instance trees would be created
In below scenario system shall behave like mentioned:
OpenOrder.request(SR, A) | SR(A)
A.request(SR, B) | SR(A) <- SR(B)
B.request(SR, A) raises immediately, because the result would be:
SR(A) <-> SR(B)
so B and A would be cyclic
"""
# Setup sufficient amount of CP
self.computer_partition_amount = 2
sequence_list = SequenceList()
sequence_string = """
# Prepare software release
LoginTestVifibDeveloper
SelectNewSoftwareReleaseUri
CreateSoftwareRelease
Tic
SubmitSoftwareRelease
Tic
CreateSoftwareProduct
Tic
ValidateSoftwareProduct
Tic
SetSoftwareProductToSoftwareRelease
PublishByActionSoftwareRelease
Logout
# Create the computer
LoginTestVifibAdmin
CreateComputer
Tic
Logout
SlapLoginCurrentComputer
FormatComputer
Tic
SlapLogout
StoreCurrentComputerReferenceBufferA
StoreCurrentComputerUidBufferA
# Install the software release
LoginTestVifibAdmin
RequestSoftwareInstallation
Tic
Logout
SlapLoginCurrentComputer
ComputerSoftwareReleaseAvailable
Tic
SlapLogout
# Create Software Instance A (originates from Open Order)
LoginTestVifibCustomer
PersonRequestSoftwareInstance
Tic
Logout
LoginDefaultUser
ConfirmOrderedSaleOrderActiveSense
Tic
SetSelectedComputerPartition
SelectCurrentlyUsedSalePackingListUid
Logout
LoginDefaultUser
CheckComputerPartitionInstanceSetupSalePackingListConfirmed
Logout
# From root request B
SelectRequestedReferenceB
SlapLoginCurrentSoftwareInstance
RequestComputerPartitionNotReadyResponse
Tic
SlapLogout
SlapLoginCurrentSoftwareInstance
RequestComputerPartition
Tic
SlapLogout
LoginDefaultUser
CheckSoftwareInstanceAndRelatedComputerPartitionNoPackingListCheck
CheckRequestedSoftwareInstanceAndRelatedComputerPartition
Logout
LoginDefaultUser
SetCurrentSoftwareInstanceRequested
SetSelectedComputerPartition
SelectCurrentlyUsedSalePackingListUid
Logout
SlapLoginCurrentSoftwareInstance
CheckRequestedComputerPartitionCleanParameterList
Logout
# Try to: From B request root
SelectRequestedReferenceRootSoftwareInstanceTitle
LoginDefaultUser # login as superuser in order to work in erp5
DirectRequestComputerPartitionRaisesValueError
"""
sequence_list.addSequenceString(sequence_string)
sequence_list.play(self)
########################################
# Software Instance graph helpers
########################################
def _test_si_tree(self):
software_instance = self.portal.software_instance_module.newContent(
portal_type='Software Instance')
self.checkConnected = software_instance.checkConnected
self.checkNotCyclic = software_instance.checkNotCyclic
def test_si_tree_simple_connected(self):
"""Graph of one element is connected
A
"""
self._test_si_tree()
graph = {'A': []}
root = 'A'
self.assertEqual(True, self.checkConnected(graph, root))
def test_si_tree_simple_list_connected(self):
"""Graph of list is connected
B->C->A
"""
self._test_si_tree()
graph = {'A': [], 'B': ['C'], 'C': ['A']}
root = 'B'
self.assertEqual(True, self.checkConnected(graph, root))
def test_si_tree_complex_connected(self):
"""Tree is connected
B --> A
\-> C --> D
\-> E --> F
"""
self._test_si_tree()
graph = {
'A': [],
'B': ['A', 'C'],
'C': ['D', 'E'],
'D': [],
'E': ['F'],
'F': [],
}
root = 'B'
self.assertEqual(True, self.checkConnected(graph, root))
def test_si_tree_simple_list_disconnected(self):
"""Two lists are disconnected
A->B
C
"""
self._test_si_tree()
graph = {'A': ['B'], 'B': [], 'C': []}
root = 'A'
from erp5.document.SoftwareInstance import DisconnectedSoftwareTree
self.assertRaises(DisconnectedSoftwareTree, self.checkConnected, graph,
root)
# For now limitation of implementation gives false positive
@expectedFailure
def test_si_tree_cyclic_connected(self):
"""Cyclic is connected
A<->B
"""
self._test_si_tree()
graph = {'A': ['B'], 'B': ['A']}
root = 'B'
self.assertEqual(True, self.checkConnected(graph, root))
def test_si_tree_cyclic_disconnected(self):
"""Two trees, where one is cyclic are disconnected
B --> A
\-> H
C --> D --> G
^ \-> E --> F \
\------------/
"""
self._test_si_tree()
graph = {
'A': [],
'B': ['A', 'H'],
'C': ['D', 'E'],
'D': ['G'],
'E': ['F'],
'F': ['C'],
'G': [],
'H': [],
}
root = 'B'
from erp5.document.SoftwareInstance import DisconnectedSoftwareTree
self.assertRaises(DisconnectedSoftwareTree, self.checkConnected, graph,
root)
def test_si_tree_simple_not_cyclic(self):
"""Graph of one element is not cyclic
A
"""
self._test_si_tree()
graph = {'A': []}
self.assertEqual(True, self.checkNotCyclic(graph))
def test_si_tree_simple_list_not_cyclic(self):
"""Graph of list is not cyclic
B->C->A
"""
self._test_si_tree()
graph = {'A': [], 'B': ['C'], 'C': ['A']}
self.assertEqual(True, self.checkNotCyclic(graph))
def test_si_tree_simple_list_cyclic(self):
"""Graph of cyclic list is cyclic
B->C->A-\
^-------/
"""
self._test_si_tree()
graph = {'A': ['B'], 'B': ['C'], 'C': ['A']}
from erp5.document.SoftwareInstance import CyclicSoftwareTree
self.assertRaises(CyclicSoftwareTree, self.checkNotCyclic, graph)
def test_si_tree_simple_list_cyclic_non_root(self):
"""Graph of cyclic list is cyclic
B->C->D->A-\
^-------/
"""
self._test_si_tree()
graph = {'A': ['C'], 'B': ['C'], 'C': ['D'], 'D': ['A']}
from erp5.document.SoftwareInstance import CyclicSoftwareTree
self.assertRaises(CyclicSoftwareTree, self.checkNotCyclic, graph)
def test_si_tree_complex_not_cyclic(self):
"""Tree is not cyclic
B --> A
\-> C --> D
\-> E --> F
"""
self._test_si_tree()
graph = {
'A': [],
'B': ['A', 'C'],
'C': ['D', 'E'],
'D': [],
'E': ['F'],
'F': [],
}
self.assertEqual(True, self.checkNotCyclic(graph))
def test_si_tree_complex_cyclic(self):
"""Tree is not cyclic
B --> A
\-> C --> D
^ \-> E --> F -\
\-------------/
"""
self._test_si_tree()
graph = {
'A': [],
'B': ['A', 'C'],
'C': ['D', 'E'],
'D': [],
'E': ['F'],
'F': ['C'],
}
from erp5.document.SoftwareInstance import CyclicSoftwareTree
self.assertRaises(CyclicSoftwareTree, self.checkNotCyclic, graph)
def test_si_tree_simple_list_disconnected_not_cyclic(self):
"""Two lists are disconnected
A->B
C
"""
self._test_si_tree()
graph = {'A': ['B'], 'B': [], 'C': []}
self.assertEqual(True, self.checkNotCyclic(graph))
def test_si_tree_cyclic(self):
"""Cyclic is connected
A<->B
"""
self._test_si_tree()
graph = {'A': ['B'], 'B': ['A']}
from erp5.document.SoftwareInstance import CyclicSoftwareTree
self.assertRaises(CyclicSoftwareTree, self.checkNotCyclic, graph)
def test_si_tree_cyclic_disconnected_cyclic(self):
"""Two trees, where one is cyclic are disconnected
B --> A
\-> H
C --> D --> G
^ \-> E --> F \
\------------/
"""
self._test_si_tree()
graph = {
'A': [],
'B': ['A', 'H'],
'C': ['D', 'E'],
'D': ['G'],
'E': ['F'],
'F': ['C'],
'G': [],
'H': ['A'],
}
from erp5.document.SoftwareInstance import CyclicSoftwareTree
self.assertRaises(CyclicSoftwareTree, self.checkNotCyclic, graph)
######################################## ########################################
# Other tests # Other tests
######################################## ########################################
def stepRequestCredentialFromWebSite(self, sequence, **kw): def stepRequestCredentialFromWebSite(self, sequence, **kw):
sequence['web_user'] = '%s.%s' % (self.id(), random()) sequence['web_user'] = '%s.%s' % (self.id(), random())
result = self.portal.ERP5Site_newCredentialRequest(\ self.portal.ERP5Site_newCredentialRequest(\
first_name='Homer', first_name='Homer',
last_name='Simpson', last_name='Simpson',
reference=sequence['web_user'], reference=sequence['web_user'],
......
...@@ -35,6 +35,7 @@ if sys.version_info < (2, 6): ...@@ -35,6 +35,7 @@ if sys.version_info < (2, 6):
import socket import socket
import subprocess import subprocess
import traceback import traceback
import time
#from time import strftime #from time import strftime
from SlapObject import Software, Partition, WrongPermissionError, \ from SlapObject import Software, Partition, WrongPermissionError, \
...@@ -106,6 +107,9 @@ def parseArgumentTupleAndReturnSlapgridObject(*argument_tuple): ...@@ -106,6 +107,9 @@ def parseArgumentTupleAndReturnSlapgridObject(*argument_tuple):
help="Enables console output and live output from subcommands.") help="Enables console output and live output from subcommands.")
parser.add_argument("-v", "--verbose", action="store_true", default=False, parser.add_argument("-v", "--verbose", action="store_true", default=False,
help="Be verbose.") help="Be verbose.")
parser.add_argument("--promise-timeout",
type=int, default=3,
help="Promise timeout in seconds.")
parser.add_argument("configuration_file", nargs=1, type=argparse.FileType(), parser.add_argument("configuration_file", nargs=1, type=argparse.FileType(),
help="SlapOS configuration file.") help="SlapOS configuration file.")
...@@ -210,7 +214,8 @@ def parseArgumentTupleAndReturnSlapgridObject(*argument_tuple): ...@@ -210,7 +214,8 @@ def parseArgumentTupleAndReturnSlapgridObject(*argument_tuple):
upload_cache_url=option_dict.get('upload-cache-url', None), upload_cache_url=option_dict.get('upload-cache-url', None),
upload_dir_url=option_dict.get('upload-dir-url', None), upload_dir_url=option_dict.get('upload-dir-url', None),
console=option_dict['console'], console=option_dict['console'],
buildout=option_dict.get('buildout')), buildout=option_dict.get('buildout'),
promise_timeout=option_dict['promise_timeout']),
option_dict]) option_dict])
...@@ -282,7 +287,8 @@ class Slapgrid(object): ...@@ -282,7 +287,8 @@ class Slapgrid(object):
upload_dir_url=None, upload_dir_url=None,
master_ca_file=None, master_ca_file=None,
certificate_repository_path=None, certificate_repository_path=None,
console=False): console=False,
promise_timeout=3):
"""Makes easy initialisation of class parameters""" """Makes easy initialisation of class parameters"""
# Parses arguments # Parses arguments
self.software_root = os.path.abspath(software_root) self.software_root = os.path.abspath(software_root)
...@@ -312,6 +318,7 @@ class Slapgrid(object): ...@@ -312,6 +318,7 @@ class Slapgrid(object):
os.path.join(self.instance_etc_directory, 'supervisord.conf.d') os.path.join(self.instance_etc_directory, 'supervisord.conf.d')
self.console = console self.console = console
self.buildout = buildout self.buildout = buildout
self.promise_timeout = promise_timeout
def checkEnvironmentAndCreateStructure(self): def checkEnvironmentAndCreateStructure(self):
"""Checks for software_root and instance_root existence, then creates """Checks for software_root and instance_root existence, then creates
...@@ -473,6 +480,53 @@ class Slapgrid(object): ...@@ -473,6 +480,53 @@ class Slapgrid(object):
exception = traceback.format_exc() exception = traceback.format_exc()
logger.error(exception) logger.error(exception)
computer_partition.error(exception) computer_partition.error(exception)
# Promises
instance_path = os.path.join(self.instance_root,
computer_partition.getId())
uid, gid = None, None
stat_info = os.stat(instance_path)
#stat sys call to get statistics informations
uid = stat_info.st_uid
gid = stat_info.st_gid
# Get the list of promises
promise_dir = os.path.join(instance_path, 'etc', 'promise')
if os.path.exists(promise_dir) and os.path.isdir(promise_dir):
cwd = instance_path
promises_list = os.listdir(promise_dir)
# Check whether every promise is kept
for promise in promises_list:
command = os.path.join(promise_dir, promise)
kw = dict()
if not self.console:
kw.update(stdout=subprocess.PIPE, stderr=subprocess.PIPE)
process_handler = SlapPopen(command,
preexec_fn=lambda: dropPrivileges(uid, gid),
cwd=cwd,
env=None, **kw)
time.sleep(self.promise_timeout)
promise = os.path.basename(command)
if process_handler.poll() is None:
process_handler.kill()
computer_partition.error("The promise %r timed out" % promise)
elif process_handler.poll() != 0:
stderr = process_handler.communicate()[1]
if stderr is None:
stderr = 'No error output from %r.' % promise
computer_partition.error(stderr)
logger.info("Finished computer partitions...") logger.info("Finished computer partitions...")
return clean_run return clean_run
......
...@@ -3,8 +3,6 @@ ...@@ -3,8 +3,6 @@
"""Mocked httplib """Mocked httplib
""" """
from urlparse import urlparse
__all__ = [] __all__ = []
def log(message): def log(message):
...@@ -83,7 +81,6 @@ class HTTPSConnection(HTTPConnection): ...@@ -83,7 +81,6 @@ class HTTPSConnection(HTTPConnection):
source_address=None): source_address=None):
super().__init__(self, host, port, strict, timeout, super().__init__(self, host, port, strict, timeout,
source_address) source_address)
pass
class HTTPResponse(object): class HTTPResponse(object):
......
...@@ -6,7 +6,9 @@ import shutil ...@@ -6,7 +6,9 @@ import shutil
import signal import signal
import slapos.slap.slap import slapos.slap.slap
import socket import socket
import sys
import tempfile import tempfile
import time
import unittest import unittest
import urlparse import urlparse
import xml_marshaller import xml_marshaller
...@@ -34,6 +36,15 @@ class BasicMixin: ...@@ -34,6 +36,15 @@ class BasicMixin:
self.buildout) self.buildout)
def tearDown(self): def tearDown(self):
# XXX: Hardcoded pid, as it is not configurable in slapos
svc = os.path.join(self.instance_root, 'var', 'run', 'supervisord.pid')
if os.path.exists(svc):
try:
pid = int(open(svc).read().strip())
except ValueError:
pass
else:
os.kill(pid, signal.SIGTERM)
shutil.rmtree(self._tempdir, True) shutil.rmtree(self._tempdir, True)
class TestBasicSlapgridCP(BasicMixin, unittest.TestCase): class TestBasicSlapgridCP(BasicMixin, unittest.TestCase):
...@@ -68,21 +79,51 @@ class MasterMixin(BasicMixin): ...@@ -68,21 +79,51 @@ class MasterMixin(BasicMixin):
setattr(httplib, name, original_value) setattr(httplib, name, original_value)
del self.saved_httplib del self.saved_httplib
def _mock_sleep(self):
self.fake_waiting_time = None
self.real_sleep = time.sleep
def mocked_sleep(secs):
if self.fake_waiting_time is not None:
secs = self.fake_waiting_time
self.real_sleep(secs)
time.sleep = mocked_sleep
def _unmock_sleep(self):
time.sleep = self.real_sleep
def _create_instance(self, name=0):
if not os.path.isdir(self.instance_root):
os.mkdir(self.instance_root)
partition_path = os.path.join(self.instance_root, str(name))
os.mkdir(partition_path, 0750)
return partition_path
def _bootstrap(self):
os.mkdir(self.software_root)
software_hash = slapos.grid.utils.getSoftwareUrlHash('http://sr/')
srdir = os.path.join(self.software_root, software_hash)
os.mkdir(srdir)
open(os.path.join(srdir, 'template.cfg'), 'w').write(
"""[buildout]""")
srbindir = os.path.join(srdir, 'bin')
os.mkdir(srbindir)
open(os.path.join(srbindir, 'buildout'), 'w').write("""#!/bin/sh
touch worked""")
os.chmod(os.path.join(srbindir, 'buildout'), 0755)
return software_hash
def setUp(self): def setUp(self):
self._patchHttplib() self._patchHttplib()
self._mock_sleep()
BasicMixin.setUp(self) BasicMixin.setUp(self)
def tearDown(self): def tearDown(self):
self._unpatchHttplib() self._unpatchHttplib()
# XXX: Hardcoded pid, as it is not configurable in slapos self._unmock_sleep()
svc = os.path.join(self.instance_root, 'var', 'run', 'supervisord.pid')
if os.path.exists(svc):
try:
pid = int(open(svc).read().strip())
except ValueError:
pass
else:
os.kill(pid, signal.SIGTERM)
BasicMixin.tearDown(self) BasicMixin.tearDown(self)
class TestSlapgridCPWithMaster(MasterMixin, unittest.TestCase): class TestSlapgridCPWithMaster(MasterMixin, unittest.TestCase):
...@@ -111,9 +152,9 @@ class TestSlapgridCPWithMaster(MasterMixin, unittest.TestCase): ...@@ -111,9 +152,9 @@ class TestSlapgridCPWithMaster(MasterMixin, unittest.TestCase):
def test_one_partition(self): def test_one_partition(self):
def server_response(self, path, method, body, header): def server_response(self, path, method, body, header):
parsed_url = urlparse.urlparse('/' + path) parsed_url = urlparse.urlparse(path.lstrip('/'))
parsed_qs = urlparse.parse_qs(parsed_url.query) parsed_qs = urlparse.parse_qs(parsed_url.query)
if parsed_url.path == '/getComputerInformation' and \ if parsed_url.path == 'getComputerInformation' and \
'computer_id' in parsed_qs: 'computer_id' in parsed_qs:
slap_computer = slapos.slap.Computer(parsed_qs['computer_id']) slap_computer = slapos.slap.Computer(parsed_qs['computer_id'])
slap_computer._software_release_list = [] slap_computer._software_release_list = []
...@@ -147,8 +188,258 @@ touch worked""") ...@@ -147,8 +188,258 @@ touch worked""")
self.assertTrue(self.grid.processComputerPartitionList()) self.assertTrue(self.grid.processComputerPartitionList())
self.assertSortedListEqual(os.listdir(self.instance_root), ['0', 'etc', self.assertSortedListEqual(os.listdir(self.instance_root), ['0', 'etc',
'var']) 'var'])
partition = os.path.join(self.instance_root, '0')
self.assertSortedListEqual(os.listdir(partition), ['worked',
'buildout.cfg'])
self.assertSortedListEqual(os.listdir(self.software_root),
[software_hash])
def test_one_partition_started(self):
def server_response(self, path, method, body, header):
parsed_url = urlparse.urlparse(path.lstrip('/'))
parsed_qs = urlparse.parse_qs(parsed_url.query)
if parsed_url.path == 'getComputerInformation' and \
'computer_id' in parsed_qs:
slap_computer = slapos.slap.Computer(parsed_qs['computer_id'])
slap_computer._software_release_list = []
partition = slapos.slap.ComputerPartition(parsed_qs['computer_id'],
'0')
partition._need_modification = True
sr = slapos.slap.SoftwareRelease()
sr._software_release = 'http://sr/'
partition._software_release_document = sr
partition._requested_state = 'started'
slap_computer._computer_partition_list = [partition]
return (200, {}, xml_marshaller.xml_marshaller.dumps(slap_computer))
else:
return (404, {}, '')
httplib.HTTPConnection._callback = server_response
os.mkdir(self.software_root)
os.mkdir(self.instance_root)
partition_path = os.path.join(self.instance_root, '0')
os.mkdir(partition_path, 0750)
software_hash = slapos.grid.utils.getSoftwareUrlHash('http://sr/')
srdir = os.path.join(self.software_root, software_hash)
os.mkdir(srdir)
open(os.path.join(srdir, 'template.cfg'), 'w').write(
"""[buildout]""")
srbindir = os.path.join(srdir, 'bin')
os.mkdir(srbindir)
open(os.path.join(srbindir, 'buildout'), 'w').write("""#!/bin/sh
touch worked &&
mkdir -p etc/run &&
echo "#!/bin/sh" > etc/run/wrapper &&
echo "while :; do echo "Working\\nWorking\\n" ; done" >> etc/run/wrapper &&
chmod 755 etc/run/wrapper
""")
os.chmod(os.path.join(srbindir, 'buildout'), 0755)
self.assertTrue(self.grid.processComputerPartitionList())
self.assertSortedListEqual(os.listdir(self.instance_root), ['0', 'etc',
'var'])
self.assertSortedListEqual(os.listdir(partition_path), ['.0_wrapper.log',
'worked', 'buildout.cfg', 'etc'])
tries = 10
wrapper_log = os.path.join(partition_path, '.0_wrapper.log')
while tries > 0:
tries -= 1
if os.path.getsize(wrapper_log) > 0:
break
time.sleep(0.2)
self.assertTrue('Working' in open(wrapper_log, 'r').read())
self.assertSortedListEqual(os.listdir(self.software_root),
[software_hash])
def test_one_partition_started_stopped(self):
def server_response(self, path, method, body, header):
parsed_url = urlparse.urlparse(path.lstrip('/'))
parsed_qs = urlparse.parse_qs(parsed_url.query)
if parsed_url.path == 'getComputerInformation' and \
'computer_id' in parsed_qs:
slap_computer = slapos.slap.Computer(parsed_qs['computer_id'])
slap_computer._software_release_list = []
partition = slapos.slap.ComputerPartition(parsed_qs['computer_id'],
'0')
partition._need_modification = True
sr = slapos.slap.SoftwareRelease()
sr._software_release = 'http://sr/'
partition._software_release_document = sr
partition._requested_state = 'started'
slap_computer._computer_partition_list = [partition]
return (200, {}, xml_marshaller.xml_marshaller.dumps(slap_computer))
else:
return (404, {}, '')
httplib.HTTPConnection._callback = server_response
os.mkdir(self.software_root)
os.mkdir(self.instance_root)
partition_path = os.path.join(self.instance_root, '0')
os.mkdir(partition_path, 0750)
software_hash = slapos.grid.utils.getSoftwareUrlHash('http://sr/')
srdir = os.path.join(self.software_root, software_hash)
os.mkdir(srdir)
open(os.path.join(srdir, 'template.cfg'), 'w').write(
"""[buildout]""")
srbindir = os.path.join(srdir, 'bin')
os.mkdir(srbindir)
open(os.path.join(srbindir, 'buildout'), 'w').write("""#!/bin/sh
touch worked &&
mkdir -p etc/run &&
(
cat <<'HEREDOC'
#!%(python)s
import signal
def handler(signum, frame):
print 'Signal handler called with signal', signum
raise SystemExit
signal.signal(signal.SIGTERM, handler)
while True:
print "Working"
HEREDOC
)> etc/run/wrapper &&
chmod 755 etc/run/wrapper
""" % dict(python = sys.executable))
os.chmod(os.path.join(srbindir, 'buildout'), 0755)
self.assertTrue(self.grid.processComputerPartitionList())
self.assertSortedListEqual(os.listdir(self.instance_root), ['0', 'etc',
'var'])
self.assertSortedListEqual(os.listdir(partition_path), ['.0_wrapper.log',
'worked', 'buildout.cfg', 'etc'])
wrapper_log = os.path.join(partition_path, '.0_wrapper.log')
tries = 10
while tries > 0:
tries -= 1
if os.path.getsize(wrapper_log) > 0:
break
time.sleep(0.2)
last_size = os.path.getsize(wrapper_log)
self.assertTrue('Working' in open(wrapper_log, 'r').read())
self.assertSortedListEqual(os.listdir(self.software_root),
[software_hash])
def server_response(self, path, method, body, header):
parsed_url = urlparse.urlparse(path.lstrip('/'))
parsed_qs = urlparse.parse_qs(parsed_url.query)
if parsed_url.path == 'getComputerInformation' and \
'computer_id' in parsed_qs:
slap_computer = slapos.slap.Computer(parsed_qs['computer_id'])
slap_computer._software_release_list = []
partition = slapos.slap.ComputerPartition(parsed_qs['computer_id'],
'0')
partition._need_modification = True
sr = slapos.slap.SoftwareRelease()
sr._software_release = 'http://sr/'
partition._software_release_document = sr
partition._requested_state = 'stopped'
slap_computer._computer_partition_list = [partition]
return (200, {}, xml_marshaller.xml_marshaller.dumps(slap_computer))
else:
return (404, {}, '')
httplib.HTTPConnection._callback = server_response
self.assertTrue(self.grid.processComputerPartitionList())
self.assertSortedListEqual(os.listdir(self.instance_root), ['0', 'etc',
'var'])
self.assertSortedListEqual(os.listdir(partition_path), ['.0_wrapper.log',
'.0_wrapper.log.1', 'worked', 'buildout.cfg', 'etc'])
tries = 10
expected_text = 'Signal handler called with signal 15'
while tries > 0:
tries -= 1
found = expected_text in open(wrapper_log, 'r').read()
if found:
break
time.sleep(0.2)
self.assertTrue(found)
def test_one_partition_stopped_started(self):
def server_response(self, path, method, body, header):
parsed_url = urlparse.urlparse(path.lstrip('/'))
parsed_qs = urlparse.parse_qs(parsed_url.query)
if parsed_url.path == 'getComputerInformation' and \
'computer_id' in parsed_qs:
slap_computer = slapos.slap.Computer(parsed_qs['computer_id'])
slap_computer._software_release_list = []
partition = slapos.slap.ComputerPartition(parsed_qs['computer_id'],
'0')
partition._need_modification = True
sr = slapos.slap.SoftwareRelease()
sr._software_release = 'http://sr/'
partition._software_release_document = sr
partition._requested_state = 'stopped'
slap_computer._computer_partition_list = [partition]
return (200, {}, xml_marshaller.xml_marshaller.dumps(slap_computer))
else:
return (404, {}, '')
httplib.HTTPConnection._callback = server_response
os.mkdir(self.software_root)
os.mkdir(self.instance_root)
partition_path = os.path.join(self.instance_root, '0')
os.mkdir(partition_path, 0750)
software_hash = slapos.grid.utils.getSoftwareUrlHash('http://sr/')
srdir = os.path.join(self.software_root, software_hash)
os.mkdir(srdir)
open(os.path.join(srdir, 'template.cfg'), 'w').write(
"""[buildout]""")
srbindir = os.path.join(srdir, 'bin')
os.mkdir(srbindir)
open(os.path.join(srbindir, 'buildout'), 'w').write("""#!/bin/sh
touch worked &&
mkdir -p etc/run &&
echo "#!/bin/sh" > etc/run/wrapper &&
echo "while :; do echo "Working\\nWorking\\n" ; done" >> etc/run/wrapper &&
chmod 755 etc/run/wrapper
""")
os.chmod(os.path.join(srbindir, 'buildout'), 0755)
self.assertTrue(self.grid.processComputerPartitionList())
self.assertSortedListEqual(os.listdir(self.instance_root), ['0', 'etc',
'var'])
partition = os.path.join(self.instance_root, '0')
self.assertSortedListEqual(os.listdir(partition), ['worked', 'etc',
'buildout.cfg'])
self.assertSortedListEqual(os.listdir(self.software_root),
[software_hash])
def server_response(self, path, method, body, header):
parsed_url = urlparse.urlparse(path.lstrip('/'))
parsed_qs = urlparse.parse_qs(parsed_url.query)
if parsed_url.path == 'getComputerInformation' and \
'computer_id' in parsed_qs:
slap_computer = slapos.slap.Computer(parsed_qs['computer_id'])
slap_computer._software_release_list = []
partition = slapos.slap.ComputerPartition(parsed_qs['computer_id'],
'0')
partition._need_modification = True
sr = slapos.slap.SoftwareRelease()
sr._software_release = 'http://sr/'
partition._software_release_document = sr
partition._requested_state = 'started'
slap_computer._computer_partition_list = [partition]
return (200, {}, xml_marshaller.xml_marshaller.dumps(slap_computer))
else:
return (404, {}, '')
httplib.HTTPConnection._callback = server_response
self.assertTrue(self.grid.processComputerPartitionList())
self.assertSortedListEqual(os.listdir(self.instance_root), ['0', 'etc',
'var'])
partition = os.path.join(self.instance_root, '0')
self.assertSortedListEqual(os.listdir(partition), ['.0_wrapper.log',
'worked', 'etc', 'buildout.cfg'])
self.assertSortedListEqual(os.listdir(self.software_root), self.assertSortedListEqual(os.listdir(self.software_root),
[software_hash]) [software_hash])
tries = 10
wrapper_log = os.path.join(partition_path, '.0_wrapper.log')
while tries > 0:
tries -= 1
if os.path.getsize(wrapper_log) > 0:
break
time.sleep(0.2)
self.assertTrue('Working' in open(wrapper_log, 'r').read())
class TestSlapgridArgumentTuple(unittest.TestCase): class TestSlapgridArgumentTuple(unittest.TestCase):
""" """
...@@ -231,3 +522,409 @@ buildout = /path/to/buildout/binary ...@@ -231,3 +522,409 @@ buildout = /path/to/buildout/binary
slapgrid_object = parser(*argument_tuple)[0] slapgrid_object = parser(*argument_tuple)[0]
self.assertEquals(self.signature_key_file_descriptor.name, self.assertEquals(self.signature_key_file_descriptor.name,
slapgrid_object.signature_private_key_file) slapgrid_object.signature_private_key_file)
class TestSlapgridCPWithMasterPromise(MasterMixin, unittest.TestCase):
def test_one_failing_promise(self):
def server_response(self_httplib, path, method, body, header):
parsed_url = urlparse.urlparse(path.lstrip('/'))
if method == 'GET':
parsed_qs = urlparse.parse_qs(parsed_url.query)
else:
parsed_qs = urlparse.parse_qs(body)
if parsed_url.path == 'getComputerInformation' and \
'computer_id' in parsed_qs:
slap_computer = slapos.slap.Computer(parsed_qs['computer_id'][0])
slap_computer._software_release_list = []
partition = slapos.slap.ComputerPartition(parsed_qs['computer_id'][0],
'0')
partition._need_modification = True
sr = slapos.slap.SoftwareRelease()
sr._software_release = 'http://sr/'
partition._software_release_document = sr
partition._requested_state = 'stopped'
slap_computer._computer_partition_list = [partition]
return (200, {}, xml_marshaller.xml_marshaller.dumps(slap_computer))
if parsed_url.path == 'softwareInstanceError' and \
method == 'POST' and 'computer_partition_id' in parsed_qs:
self.error = True
self.assertEqual(parsed_qs['computer_partition_id'][0], '0')
return (200, {}, '')
else:
return (404, {}, '')
httplib.HTTPConnection._callback = server_response
self.fake_waiting_time = 0.2
self.error = False
instance_path = self._create_instance('0')
software_hash = self._bootstrap()
promise_path = os.path.join(instance_path, 'etc', 'promise')
os.makedirs(promise_path)
fail = os.path.join(promise_path, 'fail')
worked_file = os.path.join(instance_path, 'fail_worked')
with open(fail, 'w') as f:
f.write("""#!/usr/bin/env sh
touch "%(worked_file)s"
exit 127""" % {'worked_file': worked_file})
os.chmod(fail, 0777)
self.assertTrue(self.grid.processComputerPartitionList())
self.assertTrue(os.path.isfile(worked_file))
self.assertTrue(self.error)
def test_one_succeeding_promise(self):
def server_response(self_httplib, path, method, body, header):
parsed_url = urlparse.urlparse(path.lstrip('/'))
if method == 'GET':
parsed_qs = urlparse.parse_qs(parsed_url.query)
else:
parsed_qs = urlparse.parse_qs(body)
if parsed_url.path == 'getComputerInformation' and \
'computer_id' in parsed_qs:
slap_computer = slapos.slap.Computer(parsed_qs['computer_id'][0])
slap_computer._software_release_list = []
partition = slapos.slap.ComputerPartition(parsed_qs['computer_id'][0],
'0')
partition._need_modification = True
sr = slapos.slap.SoftwareRelease()
sr._software_release = 'http://sr/'
partition._software_release_document = sr
partition._requested_state = 'stopped'
slap_computer._computer_partition_list = [partition]
return (200, {}, xml_marshaller.xml_marshaller.dumps(slap_computer))
if parsed_url.path == 'softwareInstanceError' and \
method == 'POST' and 'computer_partition_id' in parsed_qs:
self.error = True
raise AssertionError('ComputerPartition.error was raised')
return (200, {}, '')
else:
return (404, {}, '')
httplib.HTTPConnection._callback = server_response
self.fake_waiting_time = 0.2
self.error = False
instance_path = self._create_instance('0')
software_hash = self._bootstrap()
promise_path = os.path.join(instance_path, 'etc', 'promise')
os.makedirs(promise_path)
succeed = os.path.join(promise_path, 'succeed')
worked_file = os.path.join(instance_path, 'succeed_worked')
with open(succeed, 'w') as f:
f.write("""#!/usr/bin/env sh
touch "%(worked_file)s"
exit 0""" % {'worked_file': worked_file})
os.chmod(succeed, 0777)
self.assertTrue(self.grid.processComputerPartitionList())
self.assertTrue(os.path.isfile(worked_file))
self.assertFalse(self.error)
def test_stderr_has_been_sent(self):
def server_response(self_httplib, path, method, body, header):
parsed_url = urlparse.urlparse(path.lstrip('/'))
if method == 'GET':
parsed_qs = urlparse.parse_qs(parsed_url.query)
else:
parsed_qs = urlparse.parse_qs(body)
if parsed_url.path == 'getComputerInformation' and \
'computer_id' in parsed_qs:
slap_computer = slapos.slap.Computer(parsed_qs['computer_id'][0])
slap_computer._software_release_list = []
partition = slapos.slap.ComputerPartition(parsed_qs['computer_id'][0],
'0')
partition._need_modification = True
sr = slapos.slap.SoftwareRelease()
sr._software_release = 'http://sr/'
partition._software_release_document = sr
partition._requested_state = 'stopped'
slap_computer._computer_partition_list = [partition]
return (200, {}, xml_marshaller.xml_marshaller.dumps(slap_computer))
if parsed_url.path == 'softwareInstanceError' and \
method == 'POST' and 'computer_partition_id' in parsed_qs:
self.error = True
self.assertEqual(parsed_qs['computer_partition_id'][0], '0')
# XXX: Hardcoded dropPrivileges line ignore
self.error_log = '\n'.join([line for line in parsed_qs['error_log'][0].splitlines()
if 'dropPrivileges' not in line])
# end XXX
return (200, {}, '')
else:
return (404, {}, '')
httplib.HTTPConnection._callback = server_response
self.fake_waiting_time = 0.5
self.error = False
instance_path = self._create_instance('0')
software_hash = self._bootstrap()
promise_path = os.path.join(instance_path, 'etc', 'promise')
os.makedirs(promise_path)
succeed = os.path.join(promise_path, 'stderr_writer')
worked_file = os.path.join(instance_path, 'stderr_worked')
with open(succeed, 'w') as f:
f.write("""#!/usr/bin/env sh
touch "%(worked_file)s"
echo -n Error 1>&2
exit 127""" % {'worked_file': worked_file})
os.chmod(succeed, 0777)
self.assertTrue(self.grid.processComputerPartitionList())
self.assertTrue(os.path.isfile(worked_file))
self.assertEqual(self.error_log, 'Error')
self.assertTrue(self.error)
def test_timeout_works(self):
def server_response(self_httplib, path, method, body, header):
parsed_url = urlparse.urlparse(path.lstrip('/'))
if method == 'GET':
parsed_qs = urlparse.parse_qs(parsed_url.query)
else:
parsed_qs = urlparse.parse_qs(body)
if parsed_url.path == 'getComputerInformation' and \
'computer_id' in parsed_qs:
slap_computer = slapos.slap.Computer(parsed_qs['computer_id'][0])
slap_computer._software_release_list = []
partition = slapos.slap.ComputerPartition(parsed_qs['computer_id'][0],
'0')
partition._need_modification = True
sr = slapos.slap.SoftwareRelease()
sr._software_release = 'http://sr/'
partition._software_release_document = sr
partition._requested_state = 'stopped'
slap_computer._computer_partition_list = [partition]
return (200, {}, xml_marshaller.xml_marshaller.dumps(slap_computer))
if parsed_url.path == 'softwareInstanceError' and \
method == 'POST' and 'computer_partition_id' in parsed_qs:
self.error = True
self.assertEqual(parsed_qs['computer_partition_id'][0], '0')
# XXX: Hardcoded dropPrivileges line ignore
error_log = '\n'.join([line for line in parsed_qs['error_log'][0].splitlines()
if 'dropPrivileges' not in line])
# end XXX
self.assertEqual(error_log, 'The promise %r timed out' % 'timed_out_promise')
return (200, {}, '')
else:
return (404, {}, '')
httplib.HTTPConnection._callback = server_response
self.fake_waiting_time = 0.2
self.error = False
instance_path = self._create_instance('0')
software_hash = self._bootstrap()
promise_path = os.path.join(instance_path, 'etc', 'promise')
os.makedirs(promise_path)
succeed = os.path.join(promise_path, 'timed_out_promise')
worked_file = os.path.join(instance_path, 'timed_out_worked')
with open(succeed, 'w') as f:
f.write("""#!/usr/bin/env sh
touch "%(worked_file)s"
sleep 5
exit 0""" % {'worked_file': worked_file})
os.chmod(succeed, 0777)
self.assertTrue(self.grid.processComputerPartitionList())
self.assertTrue(os.path.isfile(worked_file))
self.assertTrue(self.error)
def test_two_succeeding_promises(self):
def server_response(self_httplib, path, method, body, header):
parsed_url = urlparse.urlparse(path.lstrip('/'))
if method == 'GET':
parsed_qs = urlparse.parse_qs(parsed_url.query)
else:
parsed_qs = urlparse.parse_qs(body)
if parsed_url.path == 'getComputerInformation' and \
'computer_id' in parsed_qs:
slap_computer = slapos.slap.Computer(parsed_qs['computer_id'][0])
slap_computer._software_release_list = []
partition = slapos.slap.ComputerPartition(parsed_qs['computer_id'][0],
'0')
partition._need_modification = True
sr = slapos.slap.SoftwareRelease()
sr._software_release = 'http://sr/'
partition._software_release_document = sr
partition._requested_state = 'stopped'
slap_computer._computer_partition_list = [partition]
return (200, {}, xml_marshaller.xml_marshaller.dumps(slap_computer))
if parsed_url.path == 'softwareInstanceError' and \
method == 'POST' and 'computer_partition_id' in parsed_qs:
self.error = True
raise AssertionError('ComputerPartition.error was raised')
return (200, {}, '')
else:
return (404, {}, '')
httplib.HTTPConnection._callback = server_response
self.fake_waiting_time = 0.2
self.error = False
instance_path = self._create_instance('0')
software_hash = self._bootstrap()
promise_path = os.path.join(instance_path, 'etc', 'promise')
os.makedirs(promise_path)
succeed = os.path.join(promise_path, 'succeed')
worked_file = os.path.join(instance_path, 'succeed_worked')
with open(succeed, 'w') as f:
f.write("""#!/usr/bin/env sh
touch "%(worked_file)s"
exit 0""" % {'worked_file': worked_file})
os.chmod(succeed, 0777)
succeed_2 = os.path.join(promise_path, 'succeed_2')
worked_file_2 = os.path.join(instance_path, 'succeed_2_worked')
with open(succeed_2, 'w') as f:
f.write("""#!/usr/bin/env sh
touch "%(worked_file)s"
exit 0""" % {'worked_file': worked_file_2})
os.chmod(succeed_2, 0777)
self.assertTrue(self.grid.processComputerPartitionList())
self.assertTrue(os.path.isfile(worked_file))
self.assertTrue(os.path.isfile(worked_file_2))
self.assertFalse(self.error)
def test_one_succeeding_one_failing_promises(self):
def server_response(self_httplib, path, method, body, header):
parsed_url = urlparse.urlparse(path.lstrip('/'))
if method == 'GET':
parsed_qs = urlparse.parse_qs(parsed_url.query)
else:
parsed_qs = urlparse.parse_qs(body)
if parsed_url.path == 'getComputerInformation' and \
'computer_id' in parsed_qs:
slap_computer = slapos.slap.Computer(parsed_qs['computer_id'][0])
slap_computer._software_release_list = []
partition = slapos.slap.ComputerPartition(parsed_qs['computer_id'][0],
'0')
partition._need_modification = True
sr = slapos.slap.SoftwareRelease()
sr._software_release = 'http://sr/'
partition._software_release_document = sr
partition._requested_state = 'stopped'
slap_computer._computer_partition_list = [partition]
return (200, {}, xml_marshaller.xml_marshaller.dumps(slap_computer))
if parsed_url.path == 'softwareInstanceError' and \
method == 'POST' and 'computer_partition_id' in parsed_qs:
self.error += 1
self.assertEqual(parsed_qs['computer_partition_id'][0], '0')
return (200, {}, '')
else:
return (404, {}, '')
httplib.HTTPConnection._callback = server_response
self.fake_waiting_time = 0.2
self.error = 0
instance_path = self._create_instance('0')
software_hash = self._bootstrap()
promise_path = os.path.join(instance_path, 'etc', 'promise')
os.makedirs(promise_path)
promises_files = []
for i in range(2):
promise = os.path.join(promise_path, 'promise_%d')
promises_files.append(promise)
worked_file = os.path.join(instance_path, 'promise_worked_%d')
lockfile = os.path.join(instance_path, 'lock')
with open(promise, 'w') as f:
f.write("""#!/usr/bin/env sh
touch "%(worked_file)s"
[[ ! -f "%(lockfile)s" ]] || { touch "%(lockfile)s" ; exit 127 }
exit 0""" % {'worked_file': worked_file, 'lockfile': lockfile})
os.chmod(promise, 0777)
self.assertTrue(self.grid.processComputerPartitionList())
for file_ in promises_files:
self.assertTrue(os.path.isfile(file_))
self.assertEquals(self.error, 1)
def test_one_succeeding_one_timing_out_promises(self):
def server_response(self_httplib, path, method, body, header):
parsed_url = urlparse.urlparse(path.lstrip('/'))
if method == 'GET':
parsed_qs = urlparse.parse_qs(parsed_url.query)
else:
parsed_qs = urlparse.parse_qs(body)
if parsed_url.path == 'getComputerInformation' and \
'computer_id' in parsed_qs:
slap_computer = slapos.slap.Computer(parsed_qs['computer_id'][0])
slap_computer._software_release_list = []
partition = slapos.slap.ComputerPartition(parsed_qs['computer_id'][0],
'0')
partition._need_modification = True
sr = slapos.slap.SoftwareRelease()
sr._software_release = 'http://sr/'
partition._software_release_document = sr
partition._requested_state = 'stopped'
slap_computer._computer_partition_list = [partition]
return (200, {}, xml_marshaller.xml_marshaller.dumps(slap_computer))
if parsed_url.path == 'softwareInstanceError' and \
method == 'POST' and 'computer_partition_id' in parsed_qs:
self.error += 1
self.assertEqual(parsed_qs['computer_partition_id'][0], '0')
return (200, {}, '')
else:
return (404, {}, '')
httplib.HTTPConnection._callback = server_response
self.fake_waiting_time = 0.2
self.error = 0
instance_path = self._create_instance('0')
software_hash = self._bootstrap()
promise_path = os.path.join(instance_path, 'etc', 'promise')
os.makedirs(promise_path)
promises_files = []
for i in range(2):
promise = os.path.join(promise_path, 'promise_%d')
promises_files.append(promise)
worked_file = os.path.join(instance_path, 'promise_worked_%d')
lockfile = os.path.join(instance_path, 'lock')
with open(promise, 'w') as f:
f.write("""#!/usr/bin/env sh
touch "%(worked_file)s"
[[ ! -f "%(lockfile)s" ]] || { touch "%(lockfile)s" ; sleep 5 }
exit 0""" % {'worked_file': worked_file, 'lockfile': lockfile})
os.chmod(promise, 0777)
self.assertTrue(self.grid.processComputerPartitionList())
for file_ in promises_files:
self.assertTrue(os.path.isfile(file_))
self.assertEquals(self.error, 1)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment