Commit 9a8fc64a authored by Łukasz Nowak's avatar Łukasz Nowak

Implement protection against orphaned software instances.

Treat tree of software instance as graph and simulate requested changes.
Before doing any real changes check if graph is still connected.

Add utility method to software instance class in order to use collections
module and have fast graph walking.
parent ea09a424
......@@ -29,6 +29,10 @@ from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions
from Products.ERP5.Document.Item import Item
from lxml import etree
import collections
class DisconnectedSoftwareTree(Exception):
pass
class SoftwareInstance(Item):
"""
......@@ -60,3 +64,22 @@ class SoftwareInstance(Item):
value = element.text
result_dict[key] = value
return result_dict
security.declareProtected(Permissions.AccessContentsInformation,
'checkDisconnected')
def checkDisconnected(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
......@@ -64,9 +64,12 @@ instance_xml = kwargs["instance_xml"]\n
sla_xml = kwargs["sla_xml"]\n
state = kwargs["state"]\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
while (predecessor_software_instance is not None):\n
graph[predecessor_software_instance.getUid()] = predecessor_software_instance.getPredecessorUidList()\n
root_software_instance = predecessor_software_instance\n
predecessor_software_instance = predecessor_software_instance.getPredecessorRelatedValue(\n
portal_type="Software Instance")\n
......@@ -130,6 +133,8 @@ else:\n
predecessor_software_instance.edit(\n
predecessor_uid_list=predecessor_uid_list,\n
activate_kw={\'tag\': tag},)\n
graph[predecessor_software_instance.getUid()] = predecessor_uid_list\n
\n
if state == \'started\':\n
request_software_instance.startRequested()\n
request_software_instance.activate(after_tag=tag).requestStartComputerPartition()\n
......@@ -139,7 +144,16 @@ else:\n
else:\n
raise ValueError(\'State %r is not supported\' % state)\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
# check if all elements are still connected\n
script.log(graph, root_software_instance.getUid())\n
software_instance.checkDisconnected(graph, root_software_instance.getUid())\n
\n
software_instance.edit(\n
predecessor_list=predecessor_list,\n
......
246
\ No newline at end of file
247
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment