acrn-config: add RDT support in scenario config of config app

RDT config item is located in the 5rd layer of scenario config;
the number of CLOS_MASK config is dynamically obtained by the rdt
resource clos max in the board config;
the vcpu_clos config is dynamically added or removed when the
pcpu_id config is added or removed;
the drop-down list of vcpu_clos config is reduced by half when
CDP_ENABLED=y.

Tracked-On: #4860
Signed-off-by: Shuang Zheng <shuang.zheng@intel.com>
Reviewed-by: Victor Sun <victor.sun@intel.com>
This commit is contained in:
Shuang Zheng 2020-05-29 18:30:04 +08:00 committed by Wei Liu
parent 6ed2b855bf
commit 93b12818c7
5 changed files with 249 additions and 8 deletions

View File

@ -208,6 +208,18 @@ class XmlConfig:
new_node = ElementTree.SubElement(dest_node, key, attrib={'desc': desc})
new_node.text = value
def get_curr_elem(self, *args):
"""
get elements for current path.
:param args: the path of the element.
:return: current element.
"""
if self._curr_xml_tree is None:
return
dest_node = self._get_dest_node(*args)
return dest_node
def clone_curr_elem(self, elem, *args):
"""
clone elements for current path.
@ -235,6 +247,18 @@ class XmlConfig:
dest_node = self._get_dest_node(*args)
dest_node.insert(index, elem)
def delete_curr_elem(self, *args):
"""
delete the element by its path.
:param args: the path of the element.
:return: None.
"""
if self._curr_xml_tree is None:
return
father_node = self._get_dest_node(*args[:-1])
dest_node = self._get_dest_node(*args)
father_node.remove(dest_node)
def delete_curr_key(self, *args):
"""
delete the element by its path.

View File

@ -401,6 +401,18 @@ $().ready(function(){
show_com_target(id, value);
});
$("select[ID$='FEATURES,RDT,CDP_ENABLED']").change(function(){
var id = $(this).attr('id');
var value = $(this).val();
update_vcpu_clos_option(id, value);
});
$("select[ID$='FEATURES,RDT,CDP_ENABLED']").each(function(index, item) {
var id = $(this).attr('id');
var value = $(item).val();
update_vcpu_clos_option(id, value);
});
$(document).on('click', "button:contains('+')", function() {
if($(this).text() != '+')
return;
@ -408,7 +420,21 @@ $().ready(function(){
var curr_id = curr_item_id.substr(curr_item_id.lastIndexOf('_')+1);
var config_item = $(this).parent().parent();
var config_item_added = config_item.clone();
var id_added = (parseInt(curr_id)+1).toString();
var config_vm = config_item.parent();
var vcpu_index_list = [];
config_vm.children().each(function(){
if($(this).find("button:contains('+')").size() > 0) {
var btn_add_vm_id = $(this).find("button:contains('+')").attr('id');
vcpu_index_list.push(parseInt(btn_add_vm_id.substr(btn_add_vm_id.lastIndexOf('_')+1)));
}
});
var id_added = 0;
for (i=0; i<100; i++) {
if (!vcpu_index_list.includes(i)) {
id_added = i;
break
}
}
var id_pre_added = curr_item_id.substr(0, curr_item_id.lastIndexOf('_'));
config_item_added.find("button:contains('+')").attr('id', id_pre_added+'_'+id_added);
config_item_added.find("button:contains('-')").attr('id', id_pre_added.replace('add_', 'remove_')+'_'+id_added);
@ -418,12 +444,50 @@ $().ready(function(){
config_item_added.find('.selectpicker').val('default').selectpicker('deselectAll');;
config_item_added.find('.selectpicker').selectpicker('render');
config_item_added.insertAfter(config_item);
if(curr_item_id.indexOf('add_vcpu')>=0) {
var config_vm = config_item.parent();
var curr_vcpu_index = vcpu_index_list.indexOf(parseInt(curr_id))
var vcpu_clos_item = config_vm.find("label:contains('vcpu_clos')").first().parent();
while(curr_vcpu_index > 0) {
vcpu_clos_item = vcpu_clos_item.next();
curr_vcpu_index -= 1;
}
var vcpu_clos_item_added = vcpu_clos_item.clone();
vcpu_clos_item_added.find("label:first").text("");
vcpu_clos_item_added.find('.bootstrap-select').replaceWith(function() { return $('select', this); });
vcpu_clos_item_added.find('.selectpicker').val('default').selectpicker('deselectAll');;
vcpu_clos_item_added.find('.selectpicker').selectpicker('render');
vcpu_clos_item_added.insertAfter(vcpu_clos_item);
}
});
$(document).on('click', "button:contains('-')", function() {
if($(this).text() != '-')
return;
var config_item = $(this).parent().parent();
var curr_item_id = $(this).attr('id');
if(curr_item_id.indexOf('remove_vcpu')>=0) {
var config_vm = config_item.parent();
var vcpu_index_list = [];
config_vm.children().each(function(){
if($(this).find("button:contains('+')").size() > 0) {
var btn_del_vm_id = $(this).find("button:contains('+')").attr('id');
vcpu_index_list.push(parseInt(btn_del_vm_id.substr(btn_del_vm_id.lastIndexOf('_')+1)));
}
});
var curr_item_id = $(this).attr('id');
var curr_id = parseInt(curr_item_id.substr(curr_item_id.lastIndexOf('_')+1));
curr_vcpu_index = vcpu_index_list.indexOf(curr_id);
var vcpu_clos_item = config_vm.find("label:contains('vcpu_clos')").first().parent();
while(curr_vcpu_index > 0) {
vcpu_clos_item = vcpu_clos_item.next();
curr_vcpu_index -= 1;
}
vcpu_clos_item.remove();
}
config_item.remove();
});
@ -472,6 +536,35 @@ function show_com_target(id, value) {
}
}
function update_vcpu_clos_option(id, value) {
if(value == 'y') {
$("select[ID$='clos,vcpu_clos']").each(function(){
len = $(this).find('option').length;
option = $(this).find('option').first();
for(i=0; i<len; i++){
if(i>(len-1)/2){
option.attr('disabled','disabled');
}
option = option.next();
}
$(this).selectpicker('render');
});
} else {
$("select[ID$='clos,vcpu_clos']").each(function(){
len = $(this).find('option').length;
option = $(this).find('option').first();
for(i=0; i<len; i++){
if(i>(len-1)/2){
option.removeAttr('disabled');
}
option = option.next();
}
$(this).selectpicker('render');
});
}
}
function create_setting(type, default_name, name, mode){
var board_info = $("text#board_type").text();
if (board_info==null || board_info=='') {
@ -570,7 +663,13 @@ function save_scenario(generator=null){
$("input").each(function(){
var id = $(this).attr('id');
var value = $(this).val();
if(id!='new_scenario_name' && id!='new_scenario_name2' && id!='board_info_file' && id!='board_info_upload'
if(id.indexOf('CLOS_MASK')>=0 ) {
if(id in scenario_config) {
scenario_config[id].push(value);
} else {
scenario_config[id] = [value];
}
} else if(id!='new_scenario_name' && id!='new_scenario_name2' && id!='board_info_file' && id!='board_info_upload'
&& id!='scenario_file' && id!='create_name' && id!='load_scenario_name2' && id!='load_launch_name2'
&& id!='src_path') {
scenario_config[id] = value;
@ -586,7 +685,7 @@ function save_scenario(generator=null){
$("select").each(function(){
var id = $(this).attr('id');
var value = $(this).val();
if(id.indexOf('pcpu_id')>=0 || id.indexOf('pci_dev')>=0) {
if(id.indexOf('pcpu_id')>=0 || id.indexOf('pci_dev')>=0 || id.indexOf('vcpu_clos')>=0) {
if(id in scenario_config) {
scenario_config[id].push(value);
} else {

View File

@ -179,7 +179,7 @@
</select>
</div>
</div>
<label class="col-sm-1 control-label">&nbsp;&nbsp;Board type:</label>
<label class="col-sm-1 control-label">&nbsp;&nbsp;Board name:</label>
<text class="col-sm-2 control-label" id="board_type" style="text-align: left;">{{board_type if board_type != None else ''}}</text>
<div class="col-sm-6">
<form action="" enctype="multipart/form-data" method='POST'>

View File

@ -210,6 +210,76 @@ the source files will be generated into default path and overwirte the previous
{% set first_child = [] %}
{% for sub_elem in elem.getchildren() %}
{% set sub_elem_text = sub_elem.text if sub_elem.text != None else '' %}
{% if sub_elem.tag == 'RDT' %}
{% for sub_elem_2 in sub_elem.getchildren() %}
{% if ','.join([vm.tag, elem.tag, sub_elem.tag, sub_elem_2.tag]) not in scenario_item_values %}
<div class="form-group">
<label class="col-sm-1 control-label" data-toggle="tooltip"
title="{{elem.attrib['desc'] if 'desc' in elem.attrib else elem.tag}}">
</label>
<label class="col-sm-2 control-label" data-toggle="tooltip"
title="{{sub_elem_2.attrib['desc'] if 'desc' in sub_elem_2.attrib else sub_elem.attrib['desc']}}">
{{sub_elem.tag}}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{{sub_elem_2.tag}}</label>
<div class="col-sm-6">
{% if 'readonly' in sub_elem_2.attrib and sub_elem_2.attrib['readonly'] == 'true' %}
<input type="text" class="form-control"
id="{{vm_type+','+elem.tag+','+sub_elem.tag+','+sub_elem_2.tag}}"
readonly vaule={{sub_elem_2.text}}></input>
{% else %}
<input type="text" class="form-control"
id="{{vm_type+','+elem.tag+','+sub_elem.tag+','+sub_elem_2.tag}}" value={{sub_elem_2.text}}></input>
{% endif %}
</div>
<p id="{{vm_type+','+elem.tag+','+sub_elem.tag+','+sub_elem_2.tag}}_err" class="col-sm-3"></p>
</div>
{% elif sub_elem_2.tag != 'CLOS_MASK' %}
<div class="form-group">
<label class="col-sm-1 control-label" data-toggle="tooltip"
title="{{elem.attrib['desc'] if 'desc' in elem.attrib else elem.tag}}">
</label>
<label class="col-sm-2 control-label" data-toggle="tooltip"
title="{{sub_elem_2.attrib['desc'] if 'desc' in sub_elem_2.attrib else sub_elem.attrib['desc']}}">
{{sub_elem.tag}}&nbsp;&nbsp;&nbsp;&nbsp;{{sub_elem_2.tag}}</label>
<div class="dropdown col-sm-6">
{% if 'readonly' in sub_elem_2.attrib and sub_elem_2.attrib['readonly'] == 'true' %}
<select class="selectpicker" data-width="auto"
id="{{vm_type+','+elem.tag+','+sub_elem.tag+','+sub_elem_2.tag}}" disabled>
{% else %}
<select class="selectpicker" data-width="auto"
id="{{vm_type+','+elem.tag+','+sub_elem.tag+','+sub_elem_2.tag}}">
{% endif %}
<option disabled selected value> -- Select an option -- </option>
{% for item_value in scenario_item_values[vm_type+','+elem.tag+','+sub_elem.tag+','+sub_elem_2.tag] %}
{% if item_value == sub_elem_2.text %}
<option value="{{item_value}}" selected="selected">{{item_value}}</option>
{% else %}
<option value="{{item_value}}">{{item_value}}</option>
{% endif %}
{% endfor %}
</select>
</div>
<p id="{{vm_type+','+elem.tag+','+sub_elem.tag+','+sub_elem_2.tag}}_err" class="col-sm-3"></p>
</div>
{% else %}
<div class="form-group">
<label class="col-sm-1 control-label" data-toggle="tooltip"
title="{{elem.attrib['desc'] if 'desc' in elem.attrib else elem.tag}}">
</label>
<label class="col-sm-2 control-label" data-toggle="tooltip"
title={{sub_elem_2.attrib['desc'] if 'desc' in sub_elem_2.attrib else sub_elem.attrib['desc']}}>
{{sub_elem.tag}}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{{sub_elem_2.tag}}</label>
<div class="col-sm-6">
<input type="text" class="form-control"
id="{{vm_type+','+elem.tag+','+sub_elem.tag+','+sub_elem_2.tag}}" value={{sub_elem_2.text}}></input>
</div>
<p id="{{vm_type+','+elem.tag+','+sub_elem.tag+','+sub_elem_2.tag}}_err" class="col-sm-3"></p>
</div>
{% endif %}
{% endfor %}
{% else %}
{% if 'configurable' not in sub_elem.attrib or sub_elem.attrib['configurable'] != '0' %}
<div class="form-group">
{% if 'id' not in elem.attrib %}
@ -222,6 +292,7 @@ the source files will be generated into default path and overwirte the previous
title="{{elem.attrib['desc'] if 'desc' in elem.attrib else elem.tag}}">
</label>
{% endif %}
<label class="col-sm-2 control-label" data-toggle="tooltip"
title="{{sub_elem.attrib['desc'] if 'desc' in sub_elem.attrib else sub_elem.tag}}">
{{sub_elem.tag}}</label>
@ -353,6 +424,7 @@ the source files will be generated into default path and overwirte the previous
{% endif %}
</div>
{% endif %}
{% endif %}
{% endfor %}
{% elif 'configurable' not in elem.attrib or elem.attrib['configurable'] != '0' %}
<div class="form-group">

View File

@ -147,7 +147,6 @@ def save_scenario():
:return: the error list for the edited scenario setting.
"""
scenario_config_data = request.json if request.method == "POST" else request.args
xml_configs = get_xml_configs()
board_type = xml_configs[1]
scenario_config = xml_configs[3]
@ -426,6 +425,7 @@ def create_setting():
create_name = create_config_data['create_name']
xml_configs = get_xml_configs(True)
board_info = xml_configs[0]
board_type = xml_configs[1]
scenario_config = xml_configs[2]
launch_config = xml_configs[3]
@ -470,7 +470,16 @@ def create_setting():
copyfile(src_file_name,
os.path.join(current_app.config.get('CONFIG_PATH'), board_type, 'user_defined',
create_name + '.xml'))
if mode == 'create':
# update RDT->CLOS_MASK according to board xml
scenario_config.set_curr(create_name)
rdt_clos_max = get_board_rdt_clos_max(board_info)
elem_clos_max = scenario_config.get_curr_elem('hv', 'FEATURES', 'RDT', 'CLOS_MASK')
if rdt_clos_max > 0:
for i in range(rdt_clos_max - 1):
scenario_config.clone_curr_elem(elem_clos_max, 'hv', 'FEATURES', 'RDT')
else:
scenario_config.delete_curr_elem('hv', 'FEATURES', 'RDT', 'CLOS_MASK')
scenario_config.save(create_name)
return {'status': 'success', 'setting': create_name, 'error_list': {}}
@ -616,6 +625,17 @@ def upload_board_info():
board_type))
xml_config.set_curr(generic_name[:-4])
xml_config.set_curr_attr('board', board_type)
# update RDT->CLOS_MASK according to board xml
xml_config_root = xml_config.get_curr_root()
if 'board' in xml_config_root.attrib and 'scenario' in xml_config_root.attrib \
and 'uos_launcher' not in xml_config_root.attrib:
rdt_clos_max = get_board_rdt_clos_max(filename.rsplit('.', 1)[0])
elem_clos_max = xml_config.get_curr_elem('hv', 'FEATURES', 'RDT', 'CLOS_MASK')
if rdt_clos_max > 0:
for i in range(rdt_clos_max-1):
xml_config.clone_curr_elem(elem_clos_max, 'hv', 'FEATURES', 'RDT')
else:
xml_config.delete_curr_elem('hv', 'FEATURES', 'RDT', 'CLOS_MASK')
xml_config.save(generic_name[:-4], user_defined=False)
board_info = os.path.splitext(file.filename)[0]
@ -924,7 +944,7 @@ def get_board_info(board_info):
"""
get board info type
:param board_info: the board info file
:return: the board type
:return: the board info
"""
board_config = get_board_config(board_info)
bios_info = None
@ -951,6 +971,32 @@ def get_board_info(board_info):
return (bios_info, base_board_info)
def get_board_rdt_clos_max(board_info):
"""
get board info type
:param board_info: the board info file
:return: the rdt clos max
"""
board_config = get_board_config(board_info)
rdt_clos_max = 0
if board_config is not None:
board_info_root = board_config.get_curr_root()
if board_info_root:
for item in board_info_root.getchildren():
if item.tag == 'CLOS_INFO':
for line in item.text.split('\n'):
line = line.strip()
if line.startswith('rdt resource clos max:'):
try:
rdt_clos_max = int(line.split(':')[1].strip())
break
except:
pass
return rdt_clos_max
def assign_vm_id(scenario_config):
"""
assign vm ids for the scenario setting.