Commit 2b00cb53 by Guillaume Hervier

Improve Usable Networks view for Software Release

- Add free capacity quantity property for Computer, updated by alarm
  when computing capacity scope
- Refactor said alarm script (split one big function into multiples
  small reusable scripts)
- Add "Free Instances Amount" column on "Usable Networks" view
  showing the number of free slots for a specific Software
  Release on the public computers in the network
- Add "Free Instances Amount" column on "Usable Computers" view
  showing the number of free slots for a specific Software
  Release on the computer
- Remove "Allocation Scope" column from "Usable Networks" view
  as it caused time-out for big networks
parent 3c91287f
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Standard Property" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>elementary_type/int</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>free_capacity_quantity_property</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Standard Property</string> </value>
</item>
<item>
<key> <string>property_default</string> </key>
<value> <string>python: 0</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
network = context
# XXX - The use of current authenticated person will return always 'Close' if
# the person is administrator (such as 'zope' user) but not the owner of computer
#
# person = context.ERP5Site_getAuthenticatedMemberPersonValue()
allocation_state = 'Close'
software_type = ''
filter_kw = {}
for computer in network.getSubordinationRelatedValueList():
person = computer.getSourceAdministrationValue()
filter_kw['computer_guid']=computer.getReference()
try:
isAllowed = person.Person_restrictMethodAsShadowUser(shadow_document=person,
callable_object=person.Person_findPartition,
argument_list=[software_release_url, software_type, 'Software Instance',
filter_kw],
argument_dict={'test_mode': True}
)
if isAllowed:
allocation_state = 'Open'
break
except:
continue
return allocation_state
network = context
portal = context.getPortalObject()
software_release = context.REQUEST.get('here')
if software_release is None or software_release.portal_type != 'Software Release':
software_release = portal.portal_catalog.getResultValue(
portal_type='Software Release',
url_string=software_release_url,
)
return sum([computer.Computer_getSoftwareReleaseFreePartitionCount(software_release.getUrlString())
for computer in network.getSubordinationRelatedValueList()
if computer.getAllocationScope() in ['open/public']])
......@@ -50,11 +50,11 @@
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>software_release_url</string> </value>
<value> <string>software_release_url=None, debug=False</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>ComputerNetwork_getSoftwareReleaseAllocableState</string> </value>
<value> <string>ComputerNetwork_getSoftwareReleaseFreeAmount</string> </value>
</item>
</dictionary>
</pickle>
......
......@@ -10,6 +10,7 @@ if computer.getAllocationScope() not in ['open/public', 'open/subscription']:
return
can_allocate = True
free_capacity = None
comment = ''
# First and simple way to see if computer is dead
......@@ -59,43 +60,20 @@ if can_allocate:
software_release_capacity_dict = {}
consumed_capacity = 0
def getSoftwareReleaseCapacity(instance):
def getSoftwareInstanceCapacity(instance):
software_release_url = instance.getUrlString()
software_type = instance.getSourceReference()
if software_release_url in software_release_capacity_dict:
return software_release_capacity_dict[software_release_url]
software_release = portal.portal_catalog.getResultValue(
portal_type='Software Release',
url_string={'query': software_release_url, 'key': 'ExactMatch'})
software_release_capacity = None
if software_release is not None:
# Search for Software Product Individual Variation with same reference
software_product = software_release.getAggregateValue()
if software_product is not None:
for variation in software_product.searchFolder(
portal_type="Software Product Individual Variation",
reference=software_type):
software_release_capacity = variation.getCapacityQuantity(None)
if software_release_capacity is not None:
break
if software_release_capacity is None:
software_release_capacity = software_product.getCapacityQuantity(None)
if software_release_capacity is None:
software_release_capacity = software_release.getCapacityQuantity(1)
if software_release_capacity is None:
software_release_capacity = 1
software_release_capacity = instance.SoftwareInstance_getCapacity()
software_release_capacity_dict[software_release_url] = software_release_capacity
return software_release_capacity
if allocated_instance is not None:
software_release_capacity = getSoftwareReleaseCapacity(allocated_instance)
consumed_capacity += software_release_capacity
software_instance_capacity = getSoftwareInstanceCapacity(allocated_instance)
consumed_capacity += software_instance_capacity
if consumed_capacity >= computer_capacity_quantity:
can_allocate = False
comment = 'Computer capacity limit exceeded'
......@@ -106,13 +84,15 @@ if can_allocate:
portal_type=['Software Instance', 'Slave Instance'],
validation_state='validated'):
software_release_capacity = getSoftwareReleaseCapacity(instance.getObject())
consumed_capacity += software_release_capacity
software_instance_capacity = getSoftwareInstanceCapacity(instance.getObject())
consumed_capacity += software_instance_capacity
if consumed_capacity >= computer_capacity_quantity:
can_allocate = False
comment = 'Computer capacity limit exceeded'
break
free_capacity = computer_capacity_quantity - consumed_capacity
# if can_allocate:
# result_list = portal.portal_catalog.portal_catalog(
# parent_uid=computer.getUid(),
......@@ -123,15 +103,18 @@ if can_allocate:
# can_allocate = False
# comment = 'No free partition left'
new_value = None
if can_allocate:
if computer.getCapacityScope() == 'close':
new_value = 'open'
else:
if computer.getCapacityScope() == 'open':
new_value = 'close'
if new_value is not None:
computer.edit(capacity_scope=new_value)
modified_data = {}
capacity_scope = 'open' if can_allocate else 'close'
if computer.getCapacityScope() != capacity_scope:
modified_data['capacity_scope'] = capacity_scope
if free_capacity is None or free_capacity < 0:
free_capacity = 0
if computer.getFreeCapacityQuantity() != free_capacity:
modified_data['free_capacity_quantity'] = free_capacity
if len(modified_data) > 0:
computer.edit(**modified_data)
if comment:
portal.portal_workflow.doActionFor(computer, 'edit_action', comment=comment)
computer = context
# XXX: Temporary?
if computer.getAllocationScope() not in ['open/public']:
return 0
portal = context.getPortalObject()
software_release = context.REQUEST.get('here')
if software_release is None or software_release.portal_type != 'Software Release':
software_release = portal.portal_catalog.getResultValue(
portal_type='Software Release',
url_string=software_release_url,
)
software_release_capacity = software_release.SoftwareRelease_getCapacity()
computer_free_capacity = computer.getFreeCapacityQuantity()
computer_free_partition_count = portal.portal_catalog.countResults(
parent_uid=computer.getUid(),
software_release_url=software_release.getUrlString(),
free_for_request=1,
)[0][0]
free_instance_count = min(
computer_free_partition_count,
computer_free_capacity // software_release_capacity
)
return free_instance_count
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>software_release_url=None</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Computer_getSoftwareReleaseFreePartitionCount</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -96,6 +96,7 @@
<string>my_title</string>
<string>my_capacity_quantity</string>
<string>my_capacity_scope_translated_title</string>
<string>my_free_capacity_quantity</string>
</list>
</value>
</item>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>editable</string>
<string>title</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>my_free_capacity_quantity</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>editable</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_integer_value</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string>Click to edit the target</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Free capacity</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
software_instance = context
portal = context.getPortalObject()
software_type = software_instance.getSourceReference()
software_release_url = software_instance.getUrlString()
software_release = portal.portal_catalog.getResultValue(
portal_type='Software Release',
url_string={'query': software_release_url, 'key': 'ExactMatch'})
software_instance_capacity = None
if software_release is not None:
software_product = software_release.getAggregateValue()
# Search for Software Product Individual Variation with same reference
software_product = software_release.getAggregateValue()
if software_product is not None:
for variation in software_product.searchFolder(
portal_type="Software Product Individual Variation",
reference=software_type):
software_instance_capacity = variation.getCapacityQuantity(None)
if software_instance_capacity is not None:
break
if software_instance_capacity is None:
software_instance_capacity = software_release.SoftwareRelease_getCapacity()
if software_instance_capacity is None:
software_instance_capacity = 1
return software_instance_capacity
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>SoftwareInstance_getCapacity</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
software_release = context
software_product = software_release.getAggregateValue()
software_release_capacity = software_product.getCapacityQuantity(None)
if software_release_capacity is None:
software_release_capacity = software_release.getCapacityQuantity(1)
return software_release_capacity
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>SoftwareRelease_getCapacity</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -94,6 +94,7 @@
<list>
<string>listbox_free_partition</string>
<string>listbox_amount</string>
<string>listbox_free_instance_partition_count</string>
</list>
</value>
</item>
......
......@@ -135,6 +135,10 @@
<string>Free Partition Count</string>
</tuple>
<tuple>
<string>free_instance_partition_count</string>
<string>Free Software Instance Count</string>
</tuple>
<tuple>
<string>allocation_scope_translated_title</string>
<string>Allocation Scope</string>
</tuple>
......
......@@ -11,7 +11,6 @@
<value>
<list>
<string>default</string>
<string>display_width</string>
<string>editable</string>
<string>title</string>
</list>
......@@ -19,7 +18,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>listbox_status</string> </value>
<value> <string>listbox_free_instance_partition_count</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
......@@ -44,10 +43,6 @@
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
......@@ -62,6 +57,10 @@
</value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
......@@ -70,7 +69,7 @@
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
......@@ -85,28 +84,20 @@
<value> <string></string> </value>
</item>
<item>
<key> <string>display_width</string> </key>
<value> <int>20</int> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_string_field</string> </value>
<value> <string>my_integer_field</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string>Click to edit the target</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Status</string> </value>
<value> <string>Free Instances Partitions</string> </value>
</item>
</dictionary>
</value>
......@@ -122,7 +113,7 @@
<dictionary>
<item>
<key> <string>_text</string> </key>
<value> <string>python: cell.ComputerNetwork_getSoftwareReleaseAllocableState(context.REQUEST.get(\'here\').getUrlString())</string> </value>
<value> <string>cell/Computer_getSoftwareReleaseFreePartitionCount</string> </value>
</item>
</dictionary>