Commit e9276809 authored by Rafael Monnerat's avatar Rafael Monnerat

Drop slapos_ecoallocation

See merge request nexedi/slapos.core!425
parents d8ca34a6 4409520d
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Service" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Access_contents_information_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Add_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Modify_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_View_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>product_line/cloud</string>
</tuple>
</value>
</item>
<item>
<key> <string>comment</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>IZER</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>zero_emission_ratio</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Service</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Zero Emission Ratio</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
"""
For all software releases, upgrade the Average CPU and Memory Capacity
"""
portal = context.getPortalObject()
portal.portal_catalog.searchAndActivate(
portal_type="Software Release",
method_id="SoftwareRelease_updateCapacityQuantity",
)
<?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>Alarm_updateSoftwareReleaseCapacity</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
from zExceptions import Unauthorized
if REQUEST is not None:
raise Unauthorized("Unauthorized call script from URL")
model_id = context.getWattConsumptionModel("no_model")
######
# Introduce your Consumption Model here
######
def consumption_model_shuttle_ds61_i7(load):
""" Expected consumed watts for the compute_node load
"""
if load <= 25:
return 21.5 + 1.06*load
else:
return 48 + 0.29*load
def consumption_model_shuttle_nuc_i7(load):
""" Expected consumed watts for the compute_node load
"""
if load <= 25:
return 8.5 + 0.46*load
else:
return 20 + 0.08*load
def consumption_model_rikomagic_mk802iv(load):
""" Expected consumed watts for the compute_node load
"""
if load <= 25:
return 2.2 + 0.04*load
else:
return 3.2 + 0.008*load
def no_model(load):
return 0
model_map = {
"shuttle_ds61_i7" : consumption_model_shuttle_ds61_i7,
"rikomagic_mk802iv": consumption_model_rikomagic_mk802iv,
"intel_nuc_i7": consumption_model_shuttle_nuc_i7
}
if cpu_load_percentage is None:
cpu_load_percentage = context.ComputeNode_getLatestCPUPercentLoad()
cpu_load_percentage += partition_increment
return model_map.get(model_id, no_model)(cpu_load_percentage)
<?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>cpu_load_percentage=None, partition_increment=0, REQUEST=None</string> </value>
</item>
<item>
<key> <string>_proxy_roles</string> </key>
<value>
<tuple>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>ComputeNode_getWattConsumption</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
from zExceptions import Unauthorized
if REQUEST is not None:
raise Unauthorized("Unauthorized call script from URL")
portal = context.getPortalObject()
resource_uid = context.service_module.zero_emission_ratio.getUid()
packing_list_line_list = portal.portal_catalog(
limit=1,
sort_on=("creation_date", "DESC"),
portal_type="Sale Packing List Line",
default_resource_uid = resource_uid,
default_aggregate_uid=context.getUid())
if len(packing_list_line_list):
quantity = packing_list_line_list[0].getQuantity()
if quantity > 0:
return quantity
return 0.0
<?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>REQUEST=None</string> </value>
</item>
<item>
<key> <string>_proxy_roles</string> </key>
<value>
<tuple>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>ComputeNode_getZeroEmissionRatio</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
instance, delta_co2 = context.InstanceTree_getMostEcoEfficientSoftwareInstance()
if instance is None:
return None
master_node = instance.SoftwareInstance_getResilientMasterNode()
if master_node is None:
return None
if instance.getRelativeUrl() != master_node.getRelativeUrl():
master_delta_co2 = master_node.SoftwareInstance_getFutureDeltaCO2()
saving_ratio = (master_delta_co2-delta_co2)/master_delta_co2
return "Improve Power efficiency in %s%% by using %s instance as Main Node. We recommend you to a take over." % (int(saving_ratio*100), instance.getTitle())
return None
<?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>InstanceTree_getEcoReallocationInformation</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
min_delta_co2 = 2000
minimal_candidate = None
for software_instance in context.getSpecialiseRelatedValueList(portal_type="Software Instance"):
delta_co2 = software_instance.SoftwareInstance_getFutureDeltaCO2()
if delta_co2 != "Not Applicable":
if delta_co2 < min_delta_co2:
minimal_candidate = software_instance
min_delta_co2 = delta_co2
elif (delta_co2 == min_delta_co2) and \
(software_instance.getTitle() in ["kvm0", "runner0"]):
minimal_candidate = software_instance
return minimal_candidate, min_delta_co2
<?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>InstanceTree_getMostEcoEfficientSoftwareInstance</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
from zExceptions import Unauthorized
if REQUEST is not None:
raise Unauthorized
compute_partition_list = context.getAggregateValueList(portal_type="Compute Partition")
current_watt = context.SoftwareRelease_getDeltaCO2List(
compute_partition_list, context.SoftwareInstance_getAverageCPULoad()
)
return current_watt.keys()[0]
<?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>REQUEST=None</string> </value>
</item>
<item>
<key> <string>_proxy_roles</string> </key>
<value>
<tuple>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>SoftwareInstance_getCurrentDeltaCO2</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
from zExceptions import Unauthorized
if REQUEST is not None:
raise Unauthorized
compute_partition_list = context.getAggregateValueList(portal_type="Compute Partition")
future_watt = "Not Applicable"
master_node = context.SoftwareInstance_getResilientMasterNode()
if master_node is not None:
future_watt = context.SoftwareRelease_getDeltaCO2List(
compute_partition_list, master_node.SoftwareInstance_getAverageCPULoad()
)
future_watt = future_watt.keys()[0]
return future_watt
<?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>REQUEST=None</string> </value>
</item>
<item>
<key> <string>_proxy_roles</string> </key>
<value>
<tuple>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>SoftwareInstance_getFutureDeltaCO2</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
"""
This script may contains specific prototyping code for
get master done for resilience.
"""
title = context.getTitle()
if not (title.startswith("kvm") or title.startswith("runner")):
# This instance is not a clone from resilience
return None
instance_tree = context.getSpecialiseValue()
for instance in instance_tree.getSpecialiseRelatedValueList(
portal_type="Software Instance"):
if instance.getTitle() in ["kvm0", "runner0"]:
return instance
return None
<?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_getResilientMasterNode</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
compute_partition_list = context.getAggregateValueList(portal_type="Compute Partition")
master_node = context.SoftwareInstance_getResilientMasterNode()
current_watt = context.SoftwareRelease_getDeltaCO2List(
compute_partition_list, context.SoftwareInstance_getAverageCPULoad()
)
current_watt = current_watt.keys()
master_node = context.SoftwareInstance_getResilientMasterNode()
if master_node is not None:
future_watt = context.SoftwareRelease_getDeltaCO2List(
compute_partition_list, master_node.SoftwareInstance_getAverageCPULoad()
)
future_watt = future_watt.keys()[0]
return current_watt, future_watt
<?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_getWattStats</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ERP5 Form" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<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/>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>action</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>edit_order</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>encoding</string> </key>
<value> <string>UTF-8</string> </value>
</item>
<item>
<key> <string>enctype</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>group_list</string> </key>
<value>
<list>
<string>normal</string>
<string>right width100 alignr</string>
<string>normal bis</string>
<string>center</string>
<string>hidden</string>
</list>
</value>
</item>
<item>
<key> <string>groups</string> </key>
<value>
<dictionary>
<item>
<key> <string>center</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>hidden</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>normal</string> </key>
<value>
<list>
<string>your_watt_consumption</string>
<string>your_future_watt_consumption</string>
</list>
</value>
</item>
<item>
<key> <string>normal bis</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>right width100 alignr</string> </key>
<value>
<list/>
</value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>SoftwareInstance_viewCurrentConsumption</string> </value>
</item>
<item>
<key> <string>method</string> </key>
<value> <string>POST</string> </value>
</item>
<item>
<key> <string>name</string> </key>
<value> <string>SoftwareInstance_viewAsWeb</string> </value>
</item>
<item>
<key> <string>pt</string> </key>
<value> <string>form_view_editable</string> </value>
</item>
<item>
<key> <string>row_length</string> </key>
<value> <int>4</int> </value>
</item>
<item>
<key> <string>stored_encoding</string> </key>
<value> <string>UTF-8</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>unicode_mode</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>update_action</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>update_action_title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
"""
Make a list with delta CO2 values
"""
if simulated_cpu_load is not None:
partition_average_cpu_load = simulated_cpu_load
else:
partition_average_cpu_load = context.getCpuCapacityQuantity()
partition_delta_co2_dict = {}
for compute_partition in compute_partition_list:
compute_node = compute_partition.getParentValue()
compute_node_zero_emission_ratio = compute_node.ComputeNode_getZeroEmissionRatio()
compute_node_cpu_load_percentage = compute_node.ComputeNode_getLatestCPUPercentLoad()
compute_node_watt = compute_node.ComputeNode_getWattConsumption(compute_node_cpu_load_percentage)
partition_watt = compute_node.ComputeNode_getWattConsumption(
compute_node_cpu_load_percentage + partition_average_cpu_load)
delta_watt = (partition_watt-compute_node_watt)
delta_co2 = delta_watt - delta_watt*(compute_node_zero_emission_ratio/100)
if delta_co2 in partition_delta_co2_dict:
partition_delta_co2_dict[delta_co2].append(compute_partition)
else:
partition_delta_co2_dict[delta_co2] = [compute_partition]
return partition_delta_co2_dict
<?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>compute_partition_list, simulated_cpu_load=None</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>SoftwareRelease_getDeltaCO2List</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
"""
"""
edit_kw = {}
average_cpu_load = context.SoftwareRelease_getAverageConsumedCPULoad()
average_memory_usage = context.SoftwareRelease_getAverageConsumedMemory()
if average_cpu_load != context.getCpuCapacityQuantity():
edit_kw["cpu_capacity_quantity"] = average_cpu_load
if average_memory_usage != context.getMemoryCapacityQuantity():
edit_kw["memory_capacity_quantity"] = average_memory_usage
if len(edit_kw) > 0:
context.edit(**edit_kw)
<?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_updateCapacityQuantity</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -105,4 +105,5 @@ service_module/slapos_netdrive_consumption
service_module/slapos_reservation_fee
service_module/slapos_reservation_fee/**
service_module/slapos_reservation_refund
service_module/slapos_tax
\ No newline at end of file
service_module/slapos_tax
service_module/zero_emission_ratio
\ No newline at end of file
......@@ -349,7 +349,6 @@ class TestSlapOSConfigurator(SlapOSTestCaseMixin):
'erp5_web_renderjs_ui',
'erp5_web_service',
'erp5_graph_editor',
'slapos_ecoallocation',
'slapos_jio',
'slapos_l10n_zh',
'slapos_subscription_request',
......
......@@ -6,12 +6,6 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_recorded_property_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>testSlapOSConfigurator</string> </value>
......@@ -55,28 +49,13 @@
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
......@@ -89,7 +68,7 @@
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
......@@ -98,7 +77,7 @@
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Folder" module="OFS.Folder"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_local_properties</string> </key>
<value>
<tuple>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>business_template_skin_layer_priority</string> </value>
</item>
<item>
<key> <string>type</string> </key>
<value> <string>float</string> </value>
</item>
</dictionary>
</tuple>
</value>
</item>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>business_template_skin_layer_priority</string> </key>
<value> <float>90.0</float> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>slapos_ecoallocation</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
from zExceptions import Unauthorized
if REQUEST is not None:
raise Unauthorized
document = context
portal = document.getPortalObject()
result = []
tioxml_dict = document.ComputerConsumptionTioXMLFile_parseXml()
if tioxml_dict is None:
document.reject(comment="Not usable TioXML data")
else:
packing_list_dict = {}
for movement_dict in tioxml_dict["movement"]:
reference = movement_dict['reference']
if reference in packing_list_dict:
packing_list_dict[reference].append(movement_dict)
else:
packing_list_dict[reference] = [movement_dict]
compute_node = context.getContributorValue(portal_type="Compute Node")
for reference, movement_list in packing_list_dict.items():
# Time to create the PL
delivery_template = portal.restrictedTraverse(
portal.portal_preferences.getPreferredInstanceDeliveryTemplate())
delivery = delivery_template.Base_createCloneDocument(batch_mode=1)
# It had been reported for the compute_node itself so it is pure
# informative.
if compute_node.getReference() == reference:
person = compute_node.getSourceAdministrationValue(portal_type="Person")
aggregate_value_list = [compute_node]
delivery_title = "%s Information Report" % compute_node.getReference()
else:
if reference.startswith("slapuser"):
reference = reference.replace("slapuser", "slappart")
# Find the partition / software instance / user
partition = portal.portal_catalog.getResultValue(
parent_uid=compute_node.getUid(),
reference=reference,
portal_type="Compute Partition",
validation_state="validated")
assert partition.getSlapState() == 'busy'
instance = portal.portal_catalog.getResultValue(
default_aggregate_uid=partition.getUid(),
portal_type="Software Instance",
validation_state="validated")
subscription = instance.getSpecialiseValue(
portal_type="Instance Tree")
person = subscription.getDestinationSectionValue(
portal_type="Person")
aggregate_value_list = [partition, instance, subscription]
delivery_title = "%s Consumption Usage" % instance.getReference()
delivery.edit(
title=delivery_title,
destination=person.getRelativeUrl(),
destination_decision=person.getRelativeUrl(),
start_date=context.getCreationDate(),
)
result.append(delivery.getRelativeUrl())
for movement in movement_list:
service = portal.restrictedTraverse(movement['resource'])
delivery.newContent(
portal_type="Sale Packing List Line",
title=movement['title'],
quantity=movement['quantity'],
aggregate_value_list=aggregate_value_list,
resource_value=service,
quantity_unit=service.getQuantityUnit(),
)
delivery.confirm(comment="Created from %s" % context.getRelativeUrl())
delivery.start()
delivery.stop()
delivery.deliver()
delivery.startBuilding()
document.share(comment="Created packing list: %s" % result)
return result
<?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>REQUEST=None</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>ComputerConsumptionTioXMLFile_solveInvoicingGenerationEco</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
import random
from Products.ZSQLCatalog.SQLCatalog import SimpleQuery, ComplexQuery
compute_partition = None
query_kw = {
'software_release_url': software_release_url,
'portal_type': 'Compute Partition',
}
if software_instance_portal_type == "Slave Instance":
query_kw['free_for_request'] = 0
query_kw['software_type'] = software_type
elif software_instance_portal_type == "Software Instance":
query_kw['free_for_request'] = 1
else:
raise NotImplementedError("Unknown portal type %s"%
software_instance_portal_type)
# support SLA
# Explicit location
#explicit_location = False
if "computer_guid" in filter_kw:
#explicit_location = True
query_kw["parent_reference"] = SimpleQuery(parent_reference=filter_kw.pop("computer_guid"))
if "instance_guid" in filter_kw:
#explicit_location = True
instance_guid = filter_kw.pop("instance_guid")
query_kw["aggregate_related_reference"] = SimpleQuery(aggregate_related_reference=instance_guid)
if 'network_guid' in filter_kw:
network_guid = filter_kw.pop('network_guid')
query_kw["default_subordination_reference"] = SimpleQuery(default_subordination_reference=network_guid)
if computer_network_query:
if query_kw.get("default_subordination_reference"):
query_kw["default_subordination_reference"] = ComplexQuery(
query_kw["default_subordination_reference"],
computer_network_query
)
else:
query_kw["default_subordination_reference"] = computer_network_query
if "retention_delay" in filter_kw:
filter_kw.pop("retention_delay")
compute_node_base_category_list = [
'group',
'cpu_core',
'cpu_frequency',
'cpu_type',
'local_area_network_type',
'region',
'memory_size',
'memory_type',
'storage_capacity',
'storage_interface',
'storage_redundancy',
]
for base_category in compute_node_base_category_list:
if base_category in filter_kw:
category_relative_url = "%s" % filter_kw.pop(base_category)
# XXX Small protection to prevent entering strange strings
category = context.getPortalObject().portal_categories[base_category].restrictedTraverse(str(category_relative_url), None)
if category is None:
query_kw["uid"] = "-1"
else:
query_kw["%s_uid" % base_category] = category.getUid()
query_kw["capacity_scope_uid"] = context.getPortalObject().portal_categories.capacity_scope.open.getUid()
# if not explicit_location:
# # Only allocation on public compute_node
# query_kw["allocation_scope_uid"] = context.getPortalObject().portal_categories.allocation_scope.open.public.getUid()
if filter_kw.keys():
# XXX Drop all unexpected keys
query_kw["uid"] = "-1"
if test_mode:
return bool(len(context.portal_catalog(limit=1, **query_kw)))
# Get only one compute_partition per compute_node
compute_partition_list = context.portal_catalog(group_by="parent_uid", **query_kw)
software_release_list = context.portal_catalog(
portal_type="Software Release",
url_string=software_release_url
)
if len(software_release_list) == 0:
# Forbid to allocate partitions without an existing Software Release Document.
raise KeyError(len(software_release_list))
delta_co2_contribution_list = software_release_list[0].SoftwareRelease_getDeltaCO2List(compute_partition_list)
isTransitionPossible = context.getPortalObject().portal_workflow.isTransitionPossible
while len(delta_co2_contribution_list):
partition_candidate_list = delta_co2_contribution_list.pop(min(delta_co2_contribution_list))
for compute_partition_candidate in partition_candidate_list:
compute_partition_candidate = compute_partition_candidate.getObject()
if software_instance_portal_type == "Software Instance":
# Check if the compute partition can be marked as busy
if isTransitionPossible(compute_partition_candidate, 'mark_busy'):
compute_partition = compute_partition_candidate
compute_partition.markBusy()
break
elif compute_partition_candidate.getSlapState() == "busy":
# Only assign slave instance on busy partition
compute_partition = compute_partition_candidate
break
if compute_partition is None:
raise ValueError('It was not possible to find free Compute Partition')
# lock compute partition
compute_partition.serialize()
return compute_partition.getRelativeUrl()
<?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, software_type, software_instance_portal_type, filter_kw, computer_network_query=None, test_mode=False</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Person_findPartitionEcoAllocation</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
service_module/zero_emission_ratio
\ No newline at end of file
service_module/zero_emission_ratio
\ No newline at end of file
service_module/zero_emission_ratio
\ No newline at end of file
slapos_ecoallocation
\ No newline at end of file
slapos_ecoallocation
\ No newline at end of file
......@@ -190,25 +190,16 @@ def makeTestSlapOSCodingStyleTestCase(tested_business_template):
'slapos_consumption/Base_getUserConsumptionDetailList',
'slapos_consumption/Base_jumpToViewLatestDayConsumption',
'slapos_consumption/ComputeNode_getLatestCPUPercentLoad',
'slapos_consumption/ComputeNode_getWattConsumption',
'slapos_consumption/ComputeNode_getZeroEmissionRatio',
'slapos_consumption/InstanceTree_getCPUStat',
'slapos_consumption/InstanceTree_getDiskStat',
'slapos_consumption/InstanceTree_getEcoReallocationInformation',
'slapos_consumption/InstanceTree_getMemoryStat',
'slapos_consumption/InstanceTree_getMostEcoEfficientSoftwareInstance',
'slapos_consumption/InstanceTree_getResourceConsumptionDetailList',
'slapos_consumption/InstanceTree_getStatForResource',
'slapos_consumption/SaleInvoiceTransaction_generateResourceConsumptionDocument',
'slapos_consumption/SoftwareInstance_getAverageCPULoad',
'slapos_consumption/SoftwareInstance_getCurrentDeltaCO2',
'slapos_consumption/SoftwareInstance_getFutureDeltaCO2',
'slapos_consumption/SoftwareInstance_getLatestCPUPercentLoad',
'slapos_consumption/SoftwareInstance_getResilientMasterNode',
'slapos_consumption/SoftwareInstance_getWattStats',
'slapos_consumption/SoftwareInstance_getAverageCPULoad',
'slapos_consumption/SoftwareRelease_getAverageConsumedCPULoad',
'slapos_consumption/SoftwareRelease_getAverageConsumedMemory',
'slapos_consumption/SoftwareRelease_getDeltaCO2List',
'slapos_consumption/SoftwareRelease_updateCapacityQuantity',
'slapos_wechat/Base_queryWechatOrderStatusByTradeNo',
'slapos_wechat/ERP5Site_receiveWechatPaymentCallback',
......@@ -218,8 +209,6 @@ def makeTestSlapOSCodingStyleTestCase(tested_business_template):
'slapos_deploy_theme/WebPage_viewAsWeb',
'slapos_deploy_theme/WebSection_getDocumentValue',
'slapos_deploy_theme/WebSection_renderDefaultPageAsDeployScript',
'slapos_ecoallocation/ComputerConsumptionTioXMLFile_solveInvoicingGenerationEco',
'slapos_ecoallocation/Person_findPartitionEcoAllocation',
'slapos_upgrader/Base_getUpgradeBusinessTemplateList',
'slapos_upgrader/ERP5Site_upgradeERP5CoreBusinessTemplate',
'slapos_upgrader/ERP5Site_upgradeSlapOSTestUICoreBusinessTemplate',
......@@ -417,9 +406,6 @@ def makeTestSlapOSCodingStyleTestCase(tested_business_template):
document_path = "%s/%s" % (skin.getId(), document.getId())
if not found and document_path not in self.coverage_ignore_path_list:
message_list.append(document_path)
if found and document_path in self.coverage_ignore_path_list:
message_list.append("Remove %s from coverage_ignore_path_list" % document_path)
self.maxDiff = None
self.assertEqual(message_list, [])
......
......@@ -56,7 +56,6 @@ custom
erp5_font
erp5_interaction_drop
erp5_web_hal_json
slapos_ecoallocation
slapos_erp5
slapos_upgrader
slapos_base
......@@ -167,7 +166,6 @@ custom
erp5_font
erp5_interaction_drop
erp5_web_hal_json
slapos_ecoallocation
slapos_erp5
slapos_upgrader
slapos_base
......@@ -279,7 +277,6 @@ erp5_interaction_drop
slapos_deploy_theme
erp5_web_hal_json
erp5_web_renderjs_ui
slapos_ecoallocation
slapos_erp5
slapos_upgrader
slapos_base
......@@ -388,7 +385,6 @@ custom
erp5_font
erp5_interaction_drop
erp5_web_hal_json
slapos_ecoallocation
slapos_erp5
slapos_upgrader
slapos_base
......@@ -501,7 +497,6 @@ custom
erp5_font
erp5_interaction_drop
erp5_web_hal_json
slapos_ecoallocation
slapos_erp5
slapos_upgrader
slapos_base
......@@ -614,7 +609,6 @@ custom
erp5_font
erp5_interaction_drop
erp5_web_hal_json
slapos_ecoallocation
slapos_erp5
slapos_upgrader
slapos_base
......@@ -724,7 +718,6 @@ custom
erp5_font
erp5_interaction_drop
erp5_web_hal_json
slapos_ecoallocation
slapos_erp5
slapos_upgrader
slapos_base
......@@ -835,7 +828,6 @@ custom
erp5_font
erp5_interaction_drop
erp5_web_hal_json
slapos_ecoallocation
slapos_erp5
slapos_upgrader
slapos_base
......@@ -945,7 +937,6 @@ custom
erp5_font
erp5_interaction_drop
erp5_web_hal_json
slapos_ecoallocation
slapos_erp5
slapos_upgrader
slapos_base
......@@ -1055,7 +1046,6 @@ custom
erp5_font
erp5_interaction_drop
erp5_web_hal_json
slapos_ecoallocation
slapos_erp5
slapos_upgrader
slapos_rss_style
......@@ -1166,7 +1156,6 @@ custom
erp5_font
erp5_interaction_drop
erp5_web_hal_json
slapos_ecoallocation
slapos_erp5
slapos_upgrader
slapos_base
......@@ -1278,7 +1267,6 @@ custom
erp5_font
erp5_interaction_drop
erp5_web_hal_json
slapos_ecoallocation
slapos_erp5
slapos_upgrader
slapos_base
......@@ -1392,7 +1380,6 @@ erp5_interaction_drop
slapos_hal_json_style
erp5_web_hal_json
erp5_web_renderjs_ui
slapos_ecoallocation
slapos_erp5
slapos_upgrader
slapos_base
......@@ -1504,7 +1491,6 @@ erp5_interaction_drop
erp5_hal_json_restricted_style
erp5_hal_json_style
erp5_web_hal_json
slapos_ecoallocation
slapos_erp5
slapos_upgrader
slapos_base
......
......@@ -6,12 +6,6 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_recorded_property_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>testSlapOSERP5SkinSelection</string> </value>
......@@ -55,28 +49,13 @@
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
......@@ -89,7 +68,7 @@
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
......@@ -98,7 +77,7 @@
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
......
......@@ -15,7 +15,6 @@ erp5_graph_editor
slapos_accounting
slapos_category
slapos_cloud
slapos_ecoallocation
slapos_slap_tool
slapos_crm
slapos_pdm
......
......@@ -315,7 +315,6 @@ class testSlapOSMixin(ERP5TypeTestCase):
'erp5_monaco_editor',
'erp5_certificate_authority',
'slapos_cloud',
'slapos_ecoallocation',
'slapos_l10n_zh',
'slapos_subscription_request',
'slapos_slap_tool',
......
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