Commit 916e8d64 authored by Guillaume Hervier's avatar 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']])
<?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, debug=False</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>ComputerNetwork_getSoftwareReleaseFreeAmount</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
...@@ -10,6 +10,7 @@ if computer.getAllocationScope() not in ['open/public', 'open/subscription']: ...@@ -10,6 +10,7 @@ if computer.getAllocationScope() not in ['open/public', 'open/subscription']:
return return
can_allocate = True can_allocate = True
free_capacity = None
comment = '' comment = ''
# First and simple way to see if computer is dead # First and simple way to see if computer is dead
...@@ -59,43 +60,20 @@ if can_allocate: ...@@ -59,43 +60,20 @@ if can_allocate:
software_release_capacity_dict = {} software_release_capacity_dict = {}
consumed_capacity = 0 consumed_capacity = 0
def getSoftwareReleaseCapacity(instance): def getSoftwareInstanceCapacity(instance):
software_release_url = instance.getUrlString() software_release_url = instance.getUrlString()
software_type = instance.getSourceReference() software_type = instance.getSourceReference()
if software_release_url in software_release_capacity_dict: if software_release_url in software_release_capacity_dict:
return software_release_capacity_dict[software_release_url] return software_release_capacity_dict[software_release_url]
software_release = portal.portal_catalog.getResultValue( software_release_capacity = instance.SoftwareInstance_getCapacity()
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_dict[software_release_url] = software_release_capacity software_release_capacity_dict[software_release_url] = software_release_capacity
return software_release_capacity return software_release_capacity
if allocated_instance is not None: if allocated_instance is not None:
software_release_capacity = getSoftwareReleaseCapacity(allocated_instance) software_instance_capacity = getSoftwareInstanceCapacity(allocated_instance)
consumed_capacity += software_release_capacity consumed_capacity += software_instance_capacity
if consumed_capacity >= computer_capacity_quantity: if consumed_capacity >= computer_capacity_quantity:
can_allocate = False can_allocate = False
comment = 'Computer capacity limit exceeded' comment = 'Computer capacity limit exceeded'
...@@ -106,13 +84,15 @@ if can_allocate: ...@@ -106,13 +84,15 @@ if can_allocate:
portal_type=['Software Instance', 'Slave Instance'], portal_type=['Software Instance', 'Slave Instance'],
validation_state='validated'): validation_state='validated'):
software_release_capacity = getSoftwareReleaseCapacity(instance.getObject()) software_instance_capacity = getSoftwareInstanceCapacity(instance.getObject())
consumed_capacity += software_release_capacity consumed_capacity += software_instance_capacity
if consumed_capacity >= computer_capacity_quantity: if consumed_capacity >= computer_capacity_quantity:
can_allocate = False can_allocate = False
comment = 'Computer capacity limit exceeded' comment = 'Computer capacity limit exceeded'
break break
free_capacity = computer_capacity_quantity - consumed_capacity
# if can_allocate: # if can_allocate:
# result_list = portal.portal_catalog.portal_catalog( # result_list = portal.portal_catalog.portal_catalog(
# parent_uid=computer.getUid(), # parent_uid=computer.getUid(),
...@@ -123,15 +103,18 @@ if can_allocate: ...@@ -123,15 +103,18 @@ if can_allocate:
# can_allocate = False # can_allocate = False
# comment = 'No free partition left' # comment = 'No free partition left'
new_value = None modified_data = {}
if can_allocate:
if computer.getCapacityScope() == 'close': capacity_scope = 'open' if can_allocate else 'close'
new_value = 'open' if computer.getCapacityScope() != capacity_scope:
else: modified_data['capacity_scope'] = capacity_scope
if computer.getCapacityScope() == 'open':
new_value = 'close' if free_capacity is None or free_capacity < 0:
free_capacity = 0
if new_value is not None: if computer.getFreeCapacityQuantity() != free_capacity:
computer.edit(capacity_scope=new_value) modified_data['free_capacity_quantity'] = free_capacity
if len(modified_data) > 0:
computer.edit(**modified_data)
if comment: if comment:
portal.portal_workflow.doActionFor(computer, 'edit_action', comment=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_instance_capacity = computer_free_capacity // software_release_capacity
# If computer capacity is 0, it means infinite
# In this case, only the partition count will be counted
if computer.getCapacityQuantity() == 0:
computer_instance_capacity = float('inf')
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_instance_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 @@ ...@@ -96,6 +96,7 @@
<string>my_title</string> <string>my_title</string>
<string>my_capacity_quantity</string> <string>my_capacity_quantity</string>
<string>my_capacity_scope_translated_title</string> <string>my_capacity_scope_translated_title</string>
<string>my_free_capacity_quantity</string>
</list> </list>
</value> </value>
</item> </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
...@@ -50,11 +50,11 @@ ...@@ -50,11 +50,11 @@
</item> </item>
<item> <item>
<key> <string>_params</string> </key> <key> <string>_params</string> </key>
<value> <string>software_release_url</string> </value> <value> <string></string> </value>
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>ComputerNetwork_getSoftwareReleaseAllocableState</string> </value> <value> <string>SoftwareInstance_getCapacity</string> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
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 @@ ...@@ -94,6 +94,7 @@
<list> <list>
<string>listbox_free_partition</string> <string>listbox_free_partition</string>
<string>listbox_amount</string> <string>listbox_amount</string>
<string>listbox_free_instance_partition_count</string>
</list> </list>
</value> </value>
</item> </item>
......
...@@ -134,6 +134,10 @@ ...@@ -134,6 +134,10 @@
<string>free_partition</string> <string>free_partition</string>
<string>Free Partition Count</string> <string>Free Partition Count</string>
</tuple> </tuple>
<tuple>
<string>free_instance_partition_count</string>
<string>Free Software Instance Count</string>
</tuple>
<tuple> <tuple>
<string>allocation_scope_translated_title</string> <string>allocation_scope_translated_title</string>
<string>Allocation Scope</string> <string>Allocation Scope</string>
......
...@@ -11,7 +11,6 @@ ...@@ -11,7 +11,6 @@
<value> <value>
<list> <list>
<string>default</string> <string>default</string>
<string>display_width</string>
<string>editable</string> <string>editable</string>
<string>title</string> <string>title</string>
</list> </list>
...@@ -19,7 +18,7 @@ ...@@ -19,7 +18,7 @@
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>listbox_status</string> </value> <value> <string>listbox_free_instance_partition_count</string> </value>
</item> </item>
<item> <item>
<key> <string>message_values</string> </key> <key> <string>message_values</string> </key>
...@@ -44,10 +43,6 @@ ...@@ -44,10 +43,6 @@
<key> <string>form_id</string> </key> <key> <string>form_id</string> </key>
<value> <string></string> </value> <value> <string></string> </value>
</item> </item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary> </dictionary>
</value> </value>
</item> </item>
...@@ -61,6 +56,10 @@ ...@@ -61,6 +56,10 @@
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent> <persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value> </value>
</item> </item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item> <item>
<key> <string>field_id</string> </key> <key> <string>field_id</string> </key>
<value> <string></string> </value> <value> <string></string> </value>
...@@ -70,7 +69,7 @@ ...@@ -70,7 +69,7 @@
<value> <string></string> </value> <value> <string></string> </value>
</item> </item>
<item> <item>
<key> <string>target</string> </key> <key> <string>title</string> </key>
<value> <string></string> </value> <value> <string></string> </value>
</item> </item>
</dictionary> </dictionary>
...@@ -84,29 +83,21 @@ ...@@ -84,29 +83,21 @@
<key> <string>default</string> </key> <key> <string>default</string> </key>
<value> <string></string> </value> <value> <string></string> </value>
</item> </item>
<item>
<key> <string>display_width</string> </key>
<value> <int>20</int> </value>
</item>
<item> <item>
<key> <string>editable</string> </key> <key> <string>editable</string> </key>
<value> <int>0</int> </value> <value> <int>0</int> </value>
</item> </item>
<item> <item>
<key> <string>field_id</string> </key> <key> <string>field_id</string> </key>
<value> <string>my_string_field</string> </value> <value> <string>my_integer_field</string> </value>
</item> </item>
<item> <item>
<key> <string>form_id</string> </key> <key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value> <value> <string>Base_viewFieldLibrary</string> </value>
</item> </item>
<item>
<key> <string>target</string> </key>
<value> <string>Click to edit the target</string> </value>
</item>
<item> <item>
<key> <string>title</string> </key> <key> <string>title</string> </key>
<value> <string>Status</string> </value> <value> <string>Free Instances Partitions</string> </value>
</item> </item>
</dictionary> </dictionary>
</value> </value>
...@@ -122,7 +113,7 @@ ...@@ -122,7 +113,7 @@
<dictionary> <dictionary>
<item> <item>
<key> <string>_text</string> </key> <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> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
...@@ -59,7 +59,7 @@ ...@@ -59,7 +59,7 @@
<value> <value>
<list> <list>
<string>listbox_amount</string> <string>listbox_amount</string>
<string>listbox_status</string> <string>listbox_free_instances_amount</string>
</list> </list>
</value> </value>
</item> </item>
......
...@@ -93,8 +93,8 @@ ...@@ -93,8 +93,8 @@
<string>Instances Amount</string> <string>Instances Amount</string>
</tuple> </tuple>
<tuple> <tuple>
<string>status</string> <string>free_instances_amount</string>
<string>Allocation Status</string> <string>Free Instances Amount (on public computers)</string>
</tuple> </tuple>
</list> </list>
</value> </value>
......
<?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>default</string>
<string>editable</string>
<string>title</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>listbox_free_instances_amount</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>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>default</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</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>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<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>title</string> </key>
<value> <string>Free Instances Amount</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="TALESMethod" module="Products.Formulator.TALESField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_text</string> </key>
<value> <string>cell/ComputerNetwork_getSoftwareReleaseFreeAmount</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
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