diff --git a/misc/acrn-config/config_app/app.py b/misc/acrn-config/config_app/app.py new file mode 100644 index 000000000..dabc7038c --- /dev/null +++ b/misc/acrn-config/config_app/app.py @@ -0,0 +1,43 @@ +# Copyright (C) 2019 Intel Corporation. +# SPDX-License-Identifier: BSD-3-Clause + +"""Entry for config app. + +""" + +import os +import sys +import threading +import webbrowser + +# flask: Copyright 2010 Pallets +# SPDX-License-Identifier: BSD-3-Clause +# Refer to https://github.com/pallets/flask/blob/master/LICENSE.rst for the permission notice. +from flask import Flask + +# flask: Copyright (c) 2013, Marc Brinkmann +# SPDX-License-Identifier: BSD-3-Clause +# Refer to https://pypi.org/project/Flask-Bootstrap/ for the permission notice. +from flask_bootstrap import Bootstrap + +import configs +sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..')) +sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'library')) +sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', + 'board_config')) +sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', + 'scenario_config')) +sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', + 'launch_config')) +from views import CONFIG_APP + +APP = Flask(__name__) +APP.config.from_object(configs) +APP.register_blueprint(CONFIG_APP) +APP.jinja_env.add_extension('jinja2.ext.do') +Bootstrap(app=APP) + +if __name__ == '__main__': + URL = "http://127.0.0.1:5001/scenario" + threading.Timer(1, lambda: webbrowser.open(URL)).start() + APP.run(port=5001, debug=False) diff --git a/misc/acrn-config/config_app/configs.py b/misc/acrn-config/config_app/configs.py new file mode 100644 index 000000000..24d72608b --- /dev/null +++ b/misc/acrn-config/config_app/configs.py @@ -0,0 +1,13 @@ +# Copyright (C) 2019 Intel Corporation. +# SPDX-License-Identifier: BSD-3-Clause + +"""Configurations for config app. + +""" + +import os + +BOARD_INFO = None +BOARD_TYPE = None +SCENARIO = None +CONFIG_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'xmls', 'config-xmls') diff --git a/misc/acrn-config/config_app/controller.py b/misc/acrn-config/config_app/controller.py new file mode 100644 index 000000000..47570217f --- /dev/null +++ b/misc/acrn-config/config_app/controller.py @@ -0,0 +1,288 @@ +# Copyright (C) 2019 Intel Corporation. +# SPDX-License-Identifier: BSD-3-Clause + +"""Controller for config app. + +""" + +import os +import xml.etree.ElementTree as ElementTree + + +class XmlConfig: + """The core class to analyze and modify acrn config xml files""" + def __init__(self, path=None, default=True): + self._xml_path = path + self._default = default + self._curr_xml = None + self._curr_xml_tree = None + + @staticmethod + def _get_xml_type(xml_file): + """ + get the config type by file. + :param xml_file: the file path of xml file. + :return: the xml type. + :raises: ValueError, OSError, SyntaxError. + """ + xml_type = '' + if os.path.splitext(xml_file)[1] != '.xml': + return xml_type + try: + tree = ElementTree.parse(xml_file) + root = tree.getroot() + if 'uos_launcher' in root.attrib: + xml_type = 'uos_launcher' + elif 'scenario' in root.attrib: + xml_type = 'scenario' + elif 'board' in root.attrib: + xml_type = 'board' + elif 'board_setting' in root.attrib: + xml_type = 'board_setting' + except ValueError: + print('xml parse error: {}'.format(xml_file)) + xml_type = '' + except OSError: + print('xml open error: {}'.format(xml_file)) + xml_type = '' + except SyntaxError: + print('xml syntax error: {}'.format(xml_file)) + xml_type = '' + + return xml_type + + def list_all(self, xml_type=None): + """ + list all xml config files by type. + :param xml_type: the xml type. + :return: he list of xml config files. + """ + xmls = [] + user_xmls = [] + + if self._xml_path is None or not os.path.exists(self._xml_path): + return xmls, user_xmls + for test_file in os.listdir(self._xml_path): + test_file_path = os.path.join(self._xml_path, test_file) + if os.path.isfile(test_file_path): + if XmlConfig._get_xml_type(test_file_path) == xml_type: + xmls.append(os.path.splitext(test_file)[0]) + user_path = os.path.join(self._xml_path, 'user_defined') + if os.path.isdir(user_path): + for test_file in os.listdir(user_path): + test_file_path = os.path.join(user_path, test_file) + if os.path.isfile(test_file_path): + if XmlConfig._get_xml_type(test_file_path) == xml_type: + user_xmls.append(os.path.splitext(test_file)[0]) + + return xmls, user_xmls + + def set_curr(self, xml): + """ + set current xml file to analyze. + :param xml: the xml file. + :return: None. + :raises: ValueError, OSError, SyntaxError. + """ + if self._xml_path is None: + return + try: + self._curr_xml = xml + + xml_path = os.path.join(self._xml_path, self._curr_xml + '.xml') \ + if self._default \ + else os.path.join(self._xml_path, 'user_defined', self._curr_xml + '.xml') + + tree = ElementTree.parse(xml_path) + self._curr_xml_tree = tree + except ValueError: + print('xml parse error: {}'.format(xml)) + self._curr_xml = None + self._curr_xml_tree = None + except OSError: + print('xml open error: {}'.format(xml)) + self._curr_xml = None + self._curr_xml_tree = None + except SyntaxError: + print('xml syntax error: {}'.format(xml)) + self._curr_xml = None + self._curr_xml_tree = None + + def get_curr(self): + """ + get current xml config file. + :return: current xml config file name. + """ + return self._curr_xml + + def get_curr_root(self): + """ + get the xml root of current xml config file. + :return: the xml root of current xml config file. + """ + if self._curr_xml_tree is None: + return None + return self._curr_xml_tree.getroot() + + def get_curr_value(self, *args): + """ + get the value of the element by its path. + :param args: the path of the element. + :return: the value of the element. + """ + if self._curr_xml_tree is None: + return None + dest_node = self._get_dest_node(*args) + if dest_node is None: + return None + if dest_node.text is None or dest_node.text.strip() == '': + return '' + return dest_node.text + + def set_curr_value(self, value, *args): + """ + set the value of the element by its path. + :param value: the value of the element. + :param args: the path of the element. + :return: None. + """ + if self._curr_xml_tree is None: + return + dest_node = self._get_dest_node(*args) + dest_node.text = value + + def set_curr_list(self, values, *args): + """ + set a list of sub element for the element by its path. + :param values: the list of values of the element. + :param args: the path of the element. + :return: None. + """ + if self._curr_xml_tree is None: + return + tag = args[-1] + args = args[:-1] + dest_node = self._get_dest_node(*args) + for node in dest_node.getchildren(): + dest_node.remove(node) + for value in values: + new_node = ElementTree.SubElement(dest_node, tag) + new_node.text = value + + def set_curr_attr(self, attr_name, attr_value, *args): + """ + set the attribute of the element by its path. + :param attr_name: the attribute name of the element. + :param attr_value: the attribute value of the element. + :param args: the path of the element. + :return: None. + """ + if self._curr_xml_tree is None: + return + dest_node = self._get_dest_node(*args) + dest_node.attrib[attr_name] = attr_value + + def add_curr_value(self, key, desc, value, *args): + """ + add a sub element for the element by its path. + :param key: the tag of the sub element. + :param desc: the attribute desc of the sub element. + :param value: the value of the sub element. + :param args: the path of the element. + :return: None. + """ + if self._curr_xml_tree is None: + return + + dest_node = self._get_dest_node(*args) + + if key in ['vm']: + ElementTree.SubElement(dest_node, key, attrib={'id': value, 'desc': desc}) + else: + new_node = ElementTree.SubElement(dest_node, key, attrib={'desc': desc}) + new_node.text = value + + def delete_curr_key(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 + dest_node = self._get_dest_node(*args) + self._curr_xml_tree.getroot().remove(dest_node) + + def _get_dest_node(self, *args): + """ + get the destination element by its path. + :param args: the path of the element. + :return: the destination element. + """ + if self._curr_xml_tree is None: + return None + dest_node = self._curr_xml_tree.getroot() + path = '.' + for arg in args: + # tag:attr=xxx + # tag:attr + # tag + tag = None + attr_name = None + attr_value = None + if ':' not in arg: + tag = arg + elif '=' not in arg: + # tag = arg.split(':')[0] + # attr_name = arg.split(':')[1] + raise Exception('unsupported xml path: tag:attr') + else: + tag = arg.split(':')[0] + attr = arg.split(':')[1] + attr_name = attr.split('=')[0] + attr_value = attr.split('=')[1] + + if attr_value is None: + path += ("/" + tag) + else: + path += ("/" + tag + "[@" + attr_name + "='" + attr_value + "']") + + dest_node = dest_node.findall(path) + if dest_node is not None and dest_node != []: + return dest_node[0] + + raise Exception('can not find node by {} from xml'.format(args)) + + def save(self, xml=None): + """ + save current xml to file. + :param xml: the file name to save; if not specified, save current xml to default names. + :return: None. + """ + if self._curr_xml_tree is None: + return + if xml is None: + xml = self._curr_xml + + xml_path = os.path.join(self._xml_path, 'user_defined') + if not os.path.isdir(xml_path): + os.makedirs(xml_path) + + self._format_xml(self._curr_xml_tree.getroot()) + self._curr_xml_tree.write(os.path.join(xml_path, xml+'.xml'), encoding='utf-8', + xml_declaration=True, method='xml') + + def _format_xml(self, element, depth=0): + i = "\n" + depth * " " + if element: + if not element.text or not element.text.strip(): + element.text = i + " " + if not element.tail or not element.tail.strip(): + element.tail = i + for element in element: + self._format_xml(element, depth + 1) + if not element.tail or not element.tail.strip(): + element.tail = i + else: + if depth and (not element.tail or not element.tail.strip()): + element.tail = i diff --git a/misc/acrn-config/config_app/requirements b/misc/acrn-config/config_app/requirements new file mode 100644 index 000000000..49ee1a839 --- /dev/null +++ b/misc/acrn-config/config_app/requirements @@ -0,0 +1,3 @@ +Flask==1.1.1 +flask_bootstrap==3.3.7.1 + diff --git a/misc/acrn-config/config_app/static/main.js b/misc/acrn-config/config_app/static/main.js new file mode 100644 index 000000000..deb3098a5 --- /dev/null +++ b/misc/acrn-config/config_app/static/main.js @@ -0,0 +1,560 @@ +$().ready(function(){ + $("#board_info_file").change(function () { + var fileObj = $(this)[0].files[0]; + if (typeof (fileObj) == "undefined" || fileObj.size <= 0) { + alert("Upload error."); + return; + } + var file_name = $(this).val(); + var formFile = new FormData(); + formFile.append("name", file_name); + formFile.append("file", fileObj); + + + $.ajax({ + url: "../upload_board_info", + data: formFile, + type: "Post", + dataType: "json", + cache: false, + processData: false, + contentType: false, + success: function (result) { + console.log(result); + if (result.status == 'success') { + if (result.info != 'updated') { + alert('Upload successfully.\nA new board type: '+result.info+' created.'); + } else { + alert('Upload successfully.'); + } + } else { + alert(result.status); + } + + window.location.reload(); + }, + error: function(e){ + console.log(e.status); + console.log(e.responseText); + alert(e.status+'\n'+e.responseText); + } + }) + }); + + $("#scenario_file").change(function () { + var fileObj = $(this)[0].files[0]; + if (typeof (fileObj) == "undefined" || fileObj.size <= 0) { + alert("Upload error."); + return; + } + var file_name = $(this).val(); + + var formFile = new FormData(); + formFile.append("name", file_name); + formFile.append("file", fileObj); + + $.ajax({ + url: "../upload_scenario", + data: formFile, + type: "Post", + dataType: "json", + cache: false, + processData: false, + contentType: false, + success: function (result) { + console.log(result); + status = result.status; + if (status!='success') { + alert(status); + return; + } + error_list = result.error_list; + file_name = result.file_name; + rename = result.rename + if(result.rename==true) { + alert('Scenario setting existed, import successfully with a new name: '+file_name); + } else { + alert('Scenario setting import successfully with name: '+file_name); + } + window.location = 'http://' + + window.location.host+"/scenario/user_defined_" + file_name; + }, + error: function(e){ + console.log(e.status); + console.log(e.responseText); + alert(e.status+'\n'+e.responseText); + } + }) + }); + + $("#launch_file").change(function () { + var fileObj = $(this)[0].files[0]; + if (typeof (fileObj) == "undefined" || fileObj.size <= 0) { + alert("Upload error."); + return; + } + var file_name = $(this).val(); + + var formFile = new FormData(); + formFile.append("name", file_name); + formFile.append("file", fileObj); + + $.ajax({ + url: "../upload_launch", + data: formFile, + type: "Post", + dataType: "json", + cache: false, + processData: false, + contentType: false, + success: function (result) { + console.log(result); + status = result.status; + if (status!='success') { + alert(status); + return; + } + error_list = result.error_list; + file_name = result.file_name; + rename = result.rename + if(result.rename==true) { + alert('Launch setting existed, import successfully with a new name: '+file_name); + } else { + alert('Launch setting import successfully with name: '+file_name); + } + window.location = 'http://' + + window.location.host+"/launch/user_defined_" + file_name; + }, + error: function(e){ + console.log(e.status); + console.log(e.responseText); + alert(e.status+'\n'+e.responseText); + } + }) + }); + + $("select#board_info").change(function(){ + data = {board_info: $(this).val()}; + $.ajax({ + type : "POST", + contentType: "application/json;charset=UTF-8", + url : "../select_board", + data : JSON.stringify(data), + success : function(result) { + console.log(result); + window.location.reload(true); + }, + error : function(e){ + console.log(e.status); + console.log(e.responseText); + } + }); + }); + + $("input").on('blur',function(){ + $(this).parents(".form-group").removeClass("has-error"); + $(this).parents(".form-group").children("p").text(""); + }); + + $("select").on('changed.bs.select',function(){ + $(this).parents(".form-group").removeClass("has-error"); + $(this).parents(".form-group").children("p").text(""); + }) + + $('#save_board').on('click', function() { + save_board(); + }); + + $('#save_scenario').on('click', function() { + var name = $(this).data('id'); + if(name=="generate_board_src" || name=="generate_scenario_src") { + save_scenario(name); + } + else { + save_scenario(); + } + }); + + $('#remove_scenario').on('click', function() { + old_scenario_name = $("#old_scenario_name").text(); + if(old_scenario_name.indexOf('user_defined')<0) { + alert("Default scenario setting could not be deleted."); + return; + } + + var board_info = $("select#board_info").val(); + if (board_info==null || board_info=='') { + alert("Please select one board info before this operation."); + return; + } + + scenario_config = { + old_setting_name: $("#old_scenario_name").text(), + new_setting_name: $("#new_scenario_name").val() + } + + $.ajax({ + type : "POST", + contentType: "application/json;charset=UTF-8", + url : "../remove_setting", + data : JSON.stringify(scenario_config), + success : function(result) { + console.log(result); + status = result.status + info = result.info + if (status == 'success') { + alert('Remove current scenario setting from acrn-config app successfully.'); + window.location = window.location = 'http://' + + window.location.host+"/scenario"; + } else { + alert('Remove current scenario setting from acrn-config app failed:\n'+info); + } + }, + error : function(e){ + console.log(e.status); + console.log(e.responseText); + alert(e.status+'\n'+e.responseText); + } + }); + }); + + $('#save_launch').on('click', function() { + var name = $(this).data('id'); + if(name=="generate_launch_script") { + save_launch(name); + } + else { + save_launch(); + } + }); + + $('#remove_launch').on('click', function() { + old_launch_name = $("#old_launch_name").text(); + if(old_launch_name.indexOf('user_defined')<0) { + alert("Default launch setting could not be deleted."); + return; + } + + var board_info = $("select#board_info").val(); + if (board_info==null || board_info=='') { + alert("Please select one board before this operation."); + return; + } + + launch_config = { + old_setting_name: $("#old_launch_name").text(), + new_setting_name: $("#new_launch_name").val(), + } + + $.ajax({ + type : "POST", + contentType: "application/json;charset=UTF-8", + url : "../remove_setting", + data : JSON.stringify(launch_config), + success : function(result) { + console.log(result); + status = result.status + info = result.info + if (status == 'success') { + alert('Remove current launch setting from acrn-config app successfully.'); + window.location = window.location = 'http://' + + window.location.host+"/launch"; + } else { + alert('Remove current launch setting from acrn-config app failed:\n'+info); + } + }, + error : function(e){ + console.log(e.status); + console.log(e.responseText); + alert(e.status+'\n'+e.responseText); + } + }); + }); + + $('#generate_board_src').on('click', function() { + var dataId = $(this).data('id'); + $("#save_scenario").data('id', dataId); + }); + + $('#generate_scenario_src').on('click', function() { + var dataId = $(this).data('id'); + $("#save_scenario").data('id', dataId); + }); + + $('#generate_launch_script').on('click', function() { + var dataId = $(this).data('id'); + $("#save_launch").data('id', dataId); + }); + + $("select[ID$='vuart:id=1,base']").change(function(){ + + var id = $(this).attr('id'); + var value = $(this).val(); + show_com_target(id, value); + }); + + $("select[ID$='vuart:id=1,base']").each(function(index, item) { + var id = $(item).attr('id'); + var value = $(item).val(); + show_com_target(id, value); + }) + +}) + + +function show_com_target(id, value) { + + if(id==null || id=='undefined') { + return + } + var id2 = id.replace('base', 'target_vm_id'); + var jquerySpecialChars = ["~", "`", "@", "#", "%", "&", "=", "'", "\"", + ":", ";", "<", ">", ",", "/"]; + for (var i = 0; i < jquerySpecialChars.length; i++) { + id2 = id2.replace(new RegExp(jquerySpecialChars[i], + "g"), "\\" + jquerySpecialChars[i]); + } + if (value == 'INVALID_COM_BASE') { + $('#'+id2+'_label1').hide(); + $('#'+id2+'_label2').hide(); + $('#'+id2+'_config').hide(); + $('#'+id2+'_err').hide(); + } + else { + $('#'+id2+'_label1').show(); + $('#'+id2+'_label2').show(); + $('#'+id2+'_config').show(); + $('#'+id2+'_err').show(); + } +} + + +function save_scenario(generator=null){ + var board_info = $("select#board_info").val(); + if (board_info==null || board_info=='') { + alert("Please select one board info before this operation."); + return; + } + + scenario_config = { + old_scenario_name: $("#old_scenario_name").text(), + new_scenario_name: $("#new_scenario_name").val() + } + + $("input").each(function(){ + var id = $(this).attr('id'); + var value = $(this).val(); + if(id!='new_scenario_name' && id!='board_info_file' + && id!='board_info_upload' && id!="scenario_file") { + scenario_config[id] = $(this).val(); + } + }) + + $("textarea").each(function(){ + var id = $(this).attr('id'); + var value = $(this).val(); + if(id!='new_scenario_name' && id!='board_info_file' + && id!='board_info_upload' && id!="scenario_file") { + scenario_config[id] = $(this).val(); + } + }) + + $("select").each(function(){ + var id = $(this).attr('id'); + var value = $(this).val(); + if(id!='board_info') { + scenario_config[$(this).attr('id')] = $(this).val(); + } + }) + + $.ajax({ + type : "POST", + contentType: "application/json;charset=UTF-8", + url : "../save_scenario", + data : JSON.stringify(scenario_config), + success : function(result) { + error_list = result.error_list; + status = result.status; + var no_err = true; + $.each(error_list, function(index,item){ + no_err = false; + var jquerySpecialChars = ["~", "`", "@", "#", "%", "&", "=", "'", "\"", + ":", ";", "<", ">", ",", "/"]; + for (var i = 0; i < jquerySpecialChars.length; i++) { + index = index.replace(new RegExp(jquerySpecialChars[i], + "g"), "\\" + jquerySpecialChars[i]); + } + $("#"+index).parents(".form-group").addClass("has-error"); + $("#"+index+"_err").text(item); + }) + if(no_err == true && status == 'success') { + file_name = result.file_name; + if(result.rename==true) { + alert('Scenario setting existed, saved successfully with a new name: ' + +file_name+'\nto acrn-hypervisor/misc/acrn-config/xmls/config-xmls/'+board_info+'/user_defined/'); + } else { + alert('Scenario setting saved successfully with name: ' + +file_name+'\nto acrn-hypervisor/misc/acrn-config/xmls/config-xmls/'+board_info+'/user_defined/'); + } + if(generator != null) { + generator_config = { + type: generator, + board_info: $("select#board_info").val(), + board_setting: "board_setting", + scenario_setting: file_name + } + $.ajax({ + type : "POST", + contentType: "application/json;charset=UTF-8", + url : "../generate_src", + data : JSON.stringify(generator_config), + success : function(result) { + console.log(result); + status = result.status + error_list = result.error_list + if (status == 'success' && JSON.stringify(error_list)=='{}') { + alert(generator+' successfully.'); + } else { + alert(generator+' failed. \nError list:\n'+JSON.stringify(error_list)); + } + window.location = "./user_defined_" + file_name; + }, + error : function(e){ + console.log(e.status); + console.log(e.responseText); + alert(e.status+'\n'+e.responseText); + } + }); + } else { + window.location = "./user_defined_" + file_name; + } + } + else if(status != 'success') { + alert(JSON.stringify(error_list)); + } + }, + error : function(e){ + console.log(e.status); + console.log(e.responseText); + alert(e.status+'\n'+e.responseText); + } + }); +} + +function save_launch(generator=null) { + var board_info = $("select#board_info").val(); + var scenario_name = $("select#scenario_name").val(); + if (board_info==null || board_info=='' || scenario_name==null || scenario_name=='') { + alert("Please select one board and scenario before this operation."); + return; + } + + launch_config = { + old_launch_name: $("#old_launch_name").text(), + new_launch_name: $("#new_launch_name").val(), + scenario_name: scenario_name + } + + $("input").each(function(){ + var id = $(this).attr('id'); + var value = $(this).val(); + if(id!='new_launch_name' && id!='board_info_file' + && id!='board_info_upload' && id!='scenario_name' + && id!="launch_file") { + launch_config[id] = $(this).val(); + } + }) + + $("select").each(function(){ + var id = $(this).attr('id'); + var value = $(this).val(); + if(id!='board_info') { + launch_config[$(this).attr('id')] = $(this).val(); + } + }) + + $("textarea").each(function(){ + var id = $(this).attr('id'); + var value = $(this).val(); + if(id!='new_scenario_name' && id!='board_info_file' + && id!='board_info_upload' && id!="scenario_file") { + launch_config[id] = $(this).val(); + } + }) + + $.ajax({ + type : "POST", + contentType: "application/json;charset=UTF-8", + url : "../save_launch", + data : JSON.stringify(launch_config), + success : function(result) { + console.log(result); + error_list = result.error_list; + status = result.status; + + var no_err = true; + $.each(error_list, function(index,item){ + no_err = false; + var jquerySpecialChars = ["~", "`", "@", "#", "%", "&", "=", "'", "\"", + ":", ";", "<", ">", ",", "/"]; + for (var i = 0; i < jquerySpecialChars.length; i++) { + index = index.replace(new RegExp(jquerySpecialChars[i], + "g"), "\\" + jquerySpecialChars[i]); + } + $("#"+index).parents(".form-group").addClass("has-error"); + $("#"+index+"_err").text(item); + }) + if(no_err == true && status == 'success') { + file_name = result.file_name; + if(result.rename==true) { + alert('Launch setting existed, saved successfully with a new name: ' + +file_name+'\nto acrn-hypervisor/misc/acrn-config/xmls/config-xmls/'+board_info+'/user_defined/'); + } else { + alert('Launch setting saved successfully with name: ' + +file_name+'\nto acrn-hypervisor/misc/acrn-config/xmls/config-xmls/'+board_info+'/user_defined/'); + } + if(generator != null) { + generator_config = { + type: generator, + board_info: $("select#board_info").val(), + board_setting: "board_setting", + scenario_setting: $("select#scenario_name").val(), + launch_setting: file_name + } + $.ajax({ + type : "POST", + contentType: "application/json;charset=UTF-8", + url : "../generate_src", + data : JSON.stringify(generator_config), + success : function(result) { + console.log(result); + status = result.status + error_list = result.error_list + if (status == 'success' && JSON.stringify(error_list)=='{}') { + alert(generator+' successfully.'); + } else { + alert(generator+' failed. \nError list:\n'+JSON.stringify(error_list)); + } + window.location = "./user_defined_" + file_name; + }, + error : function(e){ + console.log(e.status); + console.log(e.responseText); + alert(e.status+'\n'+e.responseText); + } + }); + } else { + window.location = "./user_defined_" + file_name; + } + } + else if(status != 'success') { + alert(JSON.stringify(error_list)); + } + }, + error : function(e){ + console.log(e.status); + console.log(e.responseText); + alert(e.status+'\n'+e.responseText); + } + }); +} diff --git a/misc/acrn-config/config_app/static/styles.css b/misc/acrn-config/config_app/static/styles.css new file mode 100644 index 000000000..f5776e7d9 --- /dev/null +++ b/misc/acrn-config/config_app/static/styles.css @@ -0,0 +1,3 @@ +ul.nav li.dropdown:hover ul.dropdown-menu { + display: block; +} diff --git a/misc/acrn-config/config_app/templates/base.html b/misc/acrn-config/config_app/templates/base.html new file mode 100644 index 000000000..2cb6531b5 --- /dev/null +++ b/misc/acrn-config/config_app/templates/base.html @@ -0,0 +1,105 @@ + + + + + + {% block title %}{% endblock %} + + + + + + + + + + + +
+
+ +
+ +
+ {{board_type if board_type != None else ''}} +
+ + + + +
+
+ +
+ +{% block body_content %} + +{% endblock %} + + + + \ No newline at end of file diff --git a/misc/acrn-config/config_app/templates/launch.html b/misc/acrn-config/config_app/templates/launch.html new file mode 100644 index 000000000..609556262 --- /dev/null +++ b/misc/acrn-config/config_app/templates/launch.html @@ -0,0 +1,310 @@ +{% extends 'base.html' %} + +{% block body_content %} + + + +
+
+ +
+ {{launch}} +
+
+ {% if board_info != None %} + + + {% else %} + + + {% endif %} +
+ {% if board_info != None and root != None %} +
+ +
+
+ +
+ {% else %} +
+ +
+
+ +
+ {% endif %} +
+
+ + +
+ + {% if board_info != None and root != None %} + + {% for vm in root.getchildren() %} + + + + + {% endfor %} +
+
+ + +
+
+ {% for elem in vm.getchildren() %} + {% if elem.getchildren() == [] and ('configurable' not in elem.attrib or elem.attrib['configurable'] != + '0') %} +
+ + + {% set elem_text = '' if elem.text == None else elem.text %} + {% if ','.join(['uos', elem.tag]) not in launch_item_values %} +
+ {% if 'readonly' in elem.attrib and elem.attrib['readonly'] == 'true' %} + {% if elem.tag == 'kernel_cmdline' %} + + {% else %} + + {% endif %} + {% else %} + {% if elem.tag == 'kernel_cmdline' %} + + {% else %} + + {% endif %} + {% endif %} +
+ {% else %} + + {% endif %} +
+ {% elif elem.getchildren() != [] %} + {% if 'multiselect' not in elem.attrib or elem.attrib['multiselect'] != 'true' %} + {% set first_child = [] %} + {% for sub_elem in elem.getchildren() %} + {% set sub_elem_text = '' if sub_elem.text == None else sub_elem.text %} + {% if 'configurable' not in sub_elem.attrib or sub_elem.attrib['configurable'] != '0' %} +
+ {% if 'id' not in elem.attrib %} + {% if not first_child %} + {% do first_child.append(1) %} + + {% else %} + + {% endif %} + + + + {% if ','.join(['uos', elem.tag, sub_elem.tag]) not in launch_item_values %} +
+ {% if 'readonly' in sub_elem.attrib and sub_elem.attrib['readonly'] == 'true' %} + + {% else %} + + {% endif %} +
+ {% else %} + + {% endif %} + {% else %} + {% if not first_child %} + {% do first_child.append(1) %} + + {% else %} + + {% endif %} + + + + {% if ','.join(['uos', elem.tag, sub_elem.tag]) not in launch_item_values %} +
+ {% if 'readonly' in sub_elem.attrib and sub_elem.attrib['readonly'] == 'true' %} + + {% else %} + + {% endif %} +
+ {% else %} + + {% endif %} + {% endif %} +
+ {% endif %} + {% endfor %} + {% elif 'configurable' not in elem.attrib or elem.attrib['configurable'] != '0' %} +
+ + + +
+ {% endif %} + {% endif %} + {% endfor %} +
+ {% else %} + No setting available. Select one board info and make sure the launch xml + exists. + + {% endif %} +
+ +{% endblock %} \ No newline at end of file diff --git a/misc/acrn-config/config_app/templates/scenario.html b/misc/acrn-config/config_app/templates/scenario.html new file mode 100644 index 000000000..aae16535c --- /dev/null +++ b/misc/acrn-config/config_app/templates/scenario.html @@ -0,0 +1,322 @@ +{% extends 'base.html' %} + +{% block body_content %} + + + +
+
+ +
+ {{scenario}} +
+
+ {% if board_info != None %} + + + {% else %} + + + {% endif %} +
+ {% if board_info != None and root != None and scenario_item_values %} +
+ +
+
+ +
+
+ +
+ {% else %} +
+ +
+
+ +
+
+ +
+ {% endif %} +
+ + {% if board_info != None and root != None and scenario_item_values %} + + {% for vm in root.getchildren() %} + {% if 'configurable' not in vm.attrib or vm.attrib['configurable'] != '0'%} + + + + + {% endif %} + {% endfor %} +
+
+ + +
+
+ {% for elem in vm.getchildren() %} + {% set elem_text = elem.text if elem.text != None else '' %} + {% if elem.getchildren() == [] and ('configurable' not in elem.attrib or elem.attrib['configurable'] + != '0')%} +
+ + + + {% if ','.join(['vm', elem.tag]) not in scenario_item_values %} +
+ {% if 'readonly' in elem.attrib and elem.attrib['readonly'] == 'true' %} + + {% else %} + + {% endif %} +
+ {% else %} + + {% endif %} +

+
+ {% elif elem.getchildren() != [] and ('configurable' not in elem.attrib or elem.attrib['configurable'] + != '0')%} + {% if 'multiselect' not in elem.attrib or elem.attrib['multiselect'] != 'true' %} + {% set first_child = [] %} + {% for sub_elem in elem.getchildren() %} + {% set sub_elem_text = sub_elem.text if sub_elem.text != None else '' %} + {% if 'configurable' not in sub_elem.attrib or sub_elem.attrib['configurable'] != '0' %} +
+ {% if 'id' not in elem.attrib %} + {% if not first_child %} + {% do first_child.append(1) %} + + {% else %} + + {% endif %} + + + {% if ','.join(['vm', elem.tag, sub_elem.tag]) not in scenario_item_values %} + {% if sub_elem.tag in ['bootargs', 'kern_args'] %} +
+ {% if 'readonly' in sub_elem.attrib and sub_elem.attrib['readonly'] == 'true' %} + + {% else %} + + {% endif %} +
+ {% else %} +
+ {% if 'readonly' in sub_elem.attrib and sub_elem.attrib['readonly'] == 'true' %} + + {% else %} + + {% endif %} +
+ {% endif %} + {% else %} + + {% endif %} +

+ {% else %} + {% if not first_child %} + {% do first_child.append(1) %} + + {% else %} + + {% endif %} + + + {% if (','.join(['vm', elem.tag, sub_elem.tag]) not in scenario_item_values) and + (elem.tag!='vuart' or sub_elem.tag!='base') %} +
+ {% if 'readonly' in sub_elem.attrib and sub_elem.attrib['readonly'] == 'true' %} + + {% else %} + + {% endif %} +
+ {% else %} + + {% endif %} +

+ {% endif %} +
+ {% endif %} + {% endfor %} + {% elif 'configurable' not in elem.attrib or elem.attrib['configurable'] != '0' %} +
+ + + +

+
+ {% endif %} + {% endif %} + {% endfor %} +
+ {% else %} + No setting available. Select one board info and make sure the scenario xml + exists. + + {% endif %} +
+ +{% endblock %} diff --git a/misc/acrn-config/config_app/views.py b/misc/acrn-config/config_app/views.py new file mode 100644 index 000000000..ed18400c0 --- /dev/null +++ b/misc/acrn-config/config_app/views.py @@ -0,0 +1,672 @@ +# Copyright (C) 2019 Intel Corporation. +# SPDX-License-Identifier: BSD-3-Clause + +"""View pages for config app. + +""" + +import os +from datetime import datetime +from shutil import copyfile + +# flask: Copyright 2010 Pallets +# SPDX-License-Identifier: BSD-3-Clause +# Refer to https://github.com/pallets/flask/blob/master/LICENSE.rst for the permission notice. +from flask import request, render_template, Blueprint, redirect, url_for, current_app + +# werkzeug: Copyright 2007 Pallets +# SPDX-License-Identifier: BSD-3-Clause +# Refer to https://github.com/pallets/werkzeug/blob/master/LICENSE.rst for the permission notice. +from werkzeug.utils import secure_filename + +from controller import XmlConfig +from scenario_config.scenario_cfg_gen import get_scenario_item_values +from scenario_config.scenario_cfg_gen import validate_scenario_setting +from launch_config.launch_cfg_gen import get_launch_item_values +from launch_config.launch_cfg_gen import validate_launch_setting + + +CONFIG_APP = Blueprint('CONFIG_APP', __name__, template_folder='templates') + + +@CONFIG_APP.route('/') +def index(): + """ + render the index page + :return: the render template of index page + """ + return redirect(url_for('CONFIG_APP.scenarios')) + + +@CONFIG_APP.route('/scenario', methods=['GET']) +def scenarios(): + """ + render the scenario parent setting page + :return: the render template of scenario setting parent page + """ + board_info, board_type, scenario_config, launch_config = get_xml_configs() + print(board_info, scenario_config, launch_config) + + return render_template('scenario.html', board_info_list=get_board_list(), + board_info=board_info, board_type=board_type, + scenarios=scenario_config.list_all(xml_type='scenario'), + launches=launch_config.list_all(xml_type='uos_launcher'), + scenario='', root=None) + + +@CONFIG_APP.route('/scenario/', methods=['GET']) +def scenario(scenario_name): + """ + render the specified scenario setting page + :param scenario_name: the scenario type + :return: the render template of the specified scenario setting page + """ + + board_info, board_type, scenario_config, launch_config = \ + get_xml_configs(scenario_name.startswith('user_defined_')) + print(board_info, scenario_config, launch_config) + current_app.config.update(SCENARIO=scenario_name) + + if scenario_name.startswith('user_defined_'): + scenario_config.set_curr(scenario_name[13:]) + else: + scenario_config.set_curr(scenario_name) + + scenario_item_values = {} + if board_info is not None and board_type is not None: + scenario_file_path = os.path.join(current_app.config.get('CONFIG_PATH'), + board_type, scenario_name + '.xml') + if scenario_name.startswith('user_defined_'): + scenario_file_path = os.path.join(current_app.config.get('CONFIG_PATH'), board_type, + 'user_defined', scenario_name[13:] + '.xml') + if os.path.isfile(scenario_file_path): + scenario_item_values = get_scenario_item_values( + os.path.join(os.path.dirname(os.path.abspath(__file__)), 'res', board_info+'.xml'), + scenario_file_path) + + print('scenario_item_values: ', scenario_item_values) + + return render_template('scenario.html', board_info_list=get_board_list(), + board_info=board_info, board_type=board_type, + scenarios=scenario_config.list_all(xml_type='scenario'), + launches=launch_config.list_all(xml_type='uos_launcher'), + scenario=scenario_name, root=scenario_config.get_curr_root(), + scenario_item_values=scenario_item_values) + + +@CONFIG_APP.route('/launch', methods=['GET']) +def launches(): + """ + render the parent launch setting page + :return: the render template of launch setting page + """ + board_info, board_type, scenario_config, launch_config = get_xml_configs() + print(board_info, scenario_config, launch_config) + + return render_template('launch.html', board_info_list=get_board_list(), + board_info=board_info, board_type=board_type, + scenarios=scenario_config.list_all(xml_type='scenario'), + launches=launch_config.list_all(xml_type='uos_launcher'), + launch='', root=None) + + +@CONFIG_APP.route('/launch/', methods=['GET']) +def launch(launch_name): + """ + render the specified launch setting page + :param launch_name: the launch type + :return: the render template of specified launch setting page + """ + print('launch: ', launch_name) + board_info, board_type, scenario_config, launch_config = \ + get_xml_configs(launch_name.startswith('user_defined_')) + print(board_info, scenario_config, launch_config) + + if launch_name.startswith('user_defined_'): + launch_config.set_curr(launch_name[13:]) + else: + launch_config.set_curr(launch_name) + + launch_item_values = {} + if board_info is not None: + launch_item_values = get_launch_item_values( + os.path.join(os.path.dirname(os.path.abspath(__file__)), 'res', board_info + '.xml')) + + print('launch_item_values: ', launch_item_values) + + return render_template('launch.html', board_info_list=get_board_list(), + board_info=board_info, board_type=board_type, + scenarios=scenario_config.list_all(xml_type='scenario'), + launches=launch_config.list_all(xml_type='uos_launcher'), + launch=launch_name, root=launch_config.get_curr_root(), + scenario=current_app.config.get('SCENARIO'), + launch_item_values=launch_item_values) + + +@CONFIG_APP.route('/save_scenario', methods=['POST']) +def save_scenario(): + """ + save scenario setting. + :return: the error list for the edited scenario setting. + """ + scenario_config_data = request.json if request.method == "POST" else request.args + print("save_scenario") + print(scenario_config_data) + + xml_configs = \ + get_xml_configs(scenario_config_data['old_scenario_name'].startswith('user_defined_')) + board_type = xml_configs[1] + scenario_config = xml_configs[3] + + if board_type is None or xml_configs[0] is None: + return {'status': 'fail', + 'error_list': {'error': 'Please select the board info before this operation.'}} + + scenario_path = os.path.join(current_app.config.get('CONFIG_PATH'), board_type) + old_scenario_name = scenario_config_data['old_scenario_name'] + if scenario_config_data['old_scenario_name'].startswith('user_defined_'): + old_scenario_name = scenario_config_data['old_scenario_name'][13:] + scenario_config.set_curr(old_scenario_name) + for key in scenario_config_data: + if key not in ['old_scenario_name', 'new_scenario_name', 'board_info_file', + 'board_info_upload']: + if isinstance(scenario_config_data[key], list): + scenario_config.set_curr_list(scenario_config_data[key], *tuple(key.split(','))) + else: + scenario_config.set_curr_value(scenario_config_data[key], *tuple(key.split(','))) + + tmp_scenario_file = os.path.join(scenario_path, 'user_defined', + 'tmp_'+scenario_config_data['new_scenario_name']+'.xml') + # if os.path.isfile(tmp_scenario_file): + # os.remove(tmp_scenario_file) + scenario_config.save('tmp_'+scenario_config_data['new_scenario_name']) + + # call validate function + error_list = {} + new_scenario_name = scenario_config_data['new_scenario_name'] + rename = False + try: + (error_list, vm_info) = validate_scenario_setting( + os.path.join(os.path.dirname(os.path.abspath(__file__)), 'res', xml_configs[0]+'.xml'), + tmp_scenario_file) + print('vm_info: ', vm_info) + except Exception as error: + return {'status': 'fail', 'file_name': new_scenario_name, + 'rename': rename, 'error_list': {'error': str(error)}} + + print('error_list: ', error_list) + + if not error_list: + old_scenario_path = os.path.join(scenario_path, old_scenario_name + '.xml') + if scenario_config_data['old_scenario_name'].startswith('user_defined_'): + old_scenario_path = os.path.join(scenario_path, 'user_defined', + old_scenario_name + '.xml') + + # check name conflict + new_scenario_path = os.path.join(scenario_path, 'user_defined', + scenario_config_data['new_scenario_name']+'.xml') + if old_scenario_path != new_scenario_path and os.path.isfile(new_scenario_path): + new_scenario_name = new_scenario_name + '_' + datetime.now().strftime('%Y%m%d%H%M%S') + rename = True + + if os.path.isfile(old_scenario_path) \ + and scenario_config_data['old_scenario_name'].startswith('user_defined_'): + os.remove(old_scenario_path) + scenario_config.save(new_scenario_name) + + if os.path.isfile(tmp_scenario_file): + os.remove(tmp_scenario_file) + + return {'status': 'success', 'file_name': new_scenario_name, + 'rename': rename, 'error_list': error_list} + + +@CONFIG_APP.route('/save_launch', methods=['POST']) +def save_launch(): + """ + save launch setting. + :return: the error list of the edited launch setting. + """ + launch_config_data = request.json if request.method == "POST" else request.args + + xml_configs = \ + get_xml_configs(launch_config_data['old_launch_name'].startswith('user_defined_')) + launch_config = xml_configs[3] + + if xml_configs[1] is None or xml_configs[0] is None: + return {'status': 'fail', + 'error_list': {'error': 'Please select the board info before this operation.'}} + + old_launch_name = launch_config_data['old_launch_name'] + old_launch_path = os.path.join(current_app.config.get('CONFIG_PATH'), xml_configs[1], + old_launch_name + '.xml') + if launch_config_data['old_launch_name'].startswith('user_defined_'): + old_launch_name = launch_config_data['old_launch_name'][13:] + old_launch_path = os.path.join(current_app.config.get('CONFIG_PATH'), xml_configs[1], + 'user_defined', old_launch_name + '.xml') + launch_config.set_curr(old_launch_name) + + for key in launch_config_data: + if key not in ['old_launch_name', 'new_launch_name', 'board_info_file', + 'board_info_upload', 'scenario_name']: + if isinstance(launch_config_data[key], list): + launch_config.set_curr_list(launch_config_data[key], *tuple(key.split(','))) + else: + launch_config.set_curr_value(launch_config_data[key], *tuple(key.split(','))) + + scenario_name = launch_config_data['scenario_name'] + scenario_file_path = os.path.join(current_app.config.get('CONFIG_PATH'), + current_app.config.get('BOARD_TYPE'), scenario_name + '.xml') + if launch_config_data['scenario_name'].startswith('user_defined_'): + scenario_name = launch_config_data['scenario_name'][13:] + scenario_file_path = os.path.join(current_app.config.get('CONFIG_PATH'), + current_app.config.get('BOARD_TYPE'), + 'user_defined', scenario_name + '.xml') + launch_config.set_curr_attr('scenario', scenario_name) + + tmp_launch_file = os.path.join(current_app.config.get('CONFIG_PATH'), xml_configs[1], + 'user_defined', + 'tmp_' + launch_config_data['new_launch_name'] + '.xml') + # if os.path.isfile(tmp_launch_file): + # os.remove(tmp_launch_file) + launch_config.save('tmp_' + launch_config_data['new_launch_name']) + + # call validate function + rename = False + try: + (error_list, pthru_sel, dm_value) = validate_launch_setting( + os.path.join(os.path.dirname(os.path.abspath(__file__)), 'res', xml_configs[0]+'.xml'), + scenario_file_path, + tmp_launch_file) + print(pthru_sel, dm_value) + except Exception as error: + return {'status': 'fail', 'file_name': launch_config_data['new_launch_name'], + 'rename': rename, 'error_list': {'launch config error': str(error)}} + + print('error_list: ', error_list) + + if not error_list: + new_launch_path = os.path.join(current_app.config.get('CONFIG_PATH'), + xml_configs[1], 'user_defined', + launch_config_data['new_launch_name'] + '.xml') + # check name conflict + if old_launch_path != new_launch_path and os.path.isfile(new_launch_path): + launch_config_data['new_launch_name'] = launch_config_data['new_launch_name'] \ + + '_' + datetime.now().strftime('%Y%m%d%H%M%S') + rename = True + + if os.path.isfile(old_launch_path) \ + and launch_config_data['old_launch_name'].startswith('user_defined_'): + os.remove(old_launch_path) + launch_config.save(launch_config_data['new_launch_name']) + + if os.path.isfile(tmp_launch_file): + os.remove(tmp_launch_file) + + return {'status': 'success', 'file_name': launch_config_data['new_launch_name'], + 'rename': rename, 'error_list': error_list} + + +@CONFIG_APP.route('/remove_setting', methods=['POST']) +def remove_setting(): + """ + remove current setting from config app + :return: the return message + """ + remove_config_data = request.json if request.method == "POST" else request.args + print("*"*100+"remove_setting") + print(remove_config_data) + + old_setting_name = remove_config_data['old_setting_name'] + + if current_app.config.get('BOARD_TYPE') is None: + return {'status': 'Board info not set before remove current setting'} + + print(current_app.config.get('CONFIG_PATH'), current_app.config.get('BOARD_TYPE')) + old_setting_path = os.path.join(current_app.config.get('CONFIG_PATH'), + current_app.config.get('BOARD_TYPE'), + old_setting_name+'.xml') + if old_setting_name.startswith('user_defined_'): + old_setting_path = os.path.join(current_app.config.get('CONFIG_PATH'), + current_app.config.get('BOARD_TYPE'), + 'user_defined', + old_setting_name[13:] + '.xml') + + if os.path.isfile(old_setting_path): + os.remove(old_setting_path) + return {'status': 'success'} + + +@CONFIG_APP.route('/generate_src', methods=['POST']) +def generate_src(): + """ + generate board src or scenario src + :return: the error message + """ + generator_config_data = request.json if request.method == "POST" else request.args + print("generate_src") + print(generator_config_data) + + src_type = generator_config_data['type'] + board_info = generator_config_data['board_info'] + board_type = current_app.config.get('BOARD_TYPE') + board_info_xml = os.path.join(os.path.dirname(os.path.abspath(__file__)), + 'res', board_info+'.xml') + scenario_setting = generator_config_data['scenario_setting'] + scenario_setting_xml = os.path.join(current_app.config.get('CONFIG_PATH'), board_type, + 'user_defined', scenario_setting+'.xml') + launch_setting_xml = None + if 'launch_setting' in generator_config_data: + launch_setting = generator_config_data['launch_setting'] + launch_setting_xml = os.path.join(current_app.config.get('CONFIG_PATH'), + board_type, 'user_defined', launch_setting + '.xml') + msg = {} + error_list = {} + status = 'success' + if src_type == 'generate_board_src': + try: + from board_config.board_cfg_gen import ui_entry_api + error_list = ui_entry_api(board_info_xml, scenario_setting_xml) + except Exception as error: + status = 'fail' + error_list = {'board setting error': str(error)} + elif src_type == 'generate_scenario_src': + try: + from scenario_config.scenario_cfg_gen import ui_entry_api + error_list = ui_entry_api(board_info_xml, scenario_setting_xml) + except Exception as error: + status = 'fail' + error_list = {'scenario setting error': str(error)} + elif src_type == 'generate_launch_script': + if scenario_setting.startswith('user_defined_'): + scenario_setting = scenario_setting[13:] + scenario_setting_xml = os.path.join(current_app.config.get('CONFIG_PATH'), board_type, + 'user_defined', scenario_setting + '.xml') + else: + scenario_setting_xml = os.path.join(current_app.config.get('CONFIG_PATH'), board_type, + scenario_setting + '.xml') + + try: + from launch_config.launch_cfg_gen import ui_entry_api + error_list = ui_entry_api(board_info_xml, scenario_setting_xml, launch_setting_xml) + except Exception as error: + status = 'fail' + error_list = {'launch setting error': str(error)} + else: + status = 'fail' + error_list = {'error': 'generator type not specified'} + msg = {'status': status, 'error_list': error_list} + print(msg) + return msg + + +@CONFIG_APP.route('/upload_board_info', methods=['POST']) +def upload_board_info(): + """ + upload board info xml file + :return: the upload status + """ + if request.method == 'POST': + if 'file' not in request.files: + return {'status': 'Error: no file uploaded'} + file = request.files['file'] + if file and '.' in file.filename and file.filename.rsplit('.', 1)[1] in ['xml']: + filename = secure_filename(file.filename) + tmp_filename = 'tmp_' + filename + save_tmp_board_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), + 'res', tmp_filename) + file.save(save_tmp_board_path) + + board_type_list = [] + config_path = current_app.config.get('CONFIG_PATH') + for config_name in os.listdir(config_path): + if os.path.isdir(os.path.join(config_path, config_name)) \ + and config_name != 'generic': + board_type_list.append(config_name) + + res_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'res') + if not os.path.isdir(res_path): + os.makedirs(res_path) + + board_info_config = XmlConfig(res_path) + board_info_config.set_curr(tmp_filename.rsplit('.', 1)[0]) + board_info_root = board_info_config.get_curr_root() + board_type = None + if board_info_root and 'board' in board_info_root.attrib \ + and 'scenario' not in board_info_root.attrib \ + and 'uos_launcher' not in board_info_root.attrib: + board_type = board_info_root.attrib['board'] + if not board_type: + os.remove(save_tmp_board_path) + return {'status': 'Error on parsing Board info\n' + 'check the xml syntax and whether there is only the board ' + 'attribute in the board info file'} + + os.rename(save_tmp_board_path, + os.path.join(os.path.dirname(os.path.abspath(__file__)), + 'res', filename)) + info = 'updated' + if board_type not in board_type_list: + info = board_type + os.makedirs(os.path.join(config_path, board_type)) + for generic_name in os.listdir(os.path.join(config_path, 'generic')): + generic_file = os.path.join(config_path, 'generic', generic_name) + if os.path.isfile(generic_file): + copyfile(generic_file, os.path.join(config_path, board_type, generic_name)) + + board_info = os.path.splitext(file.filename)[0] + current_app.config.update(BOARD_INFO=board_info) + current_app.config.update(BOARD_TYPE=board_type) + + return {'status': 'success', 'info': info} + + return {'status': 'Error: upload failed'} + + +@CONFIG_APP.route('/select_board', methods=['POST']) +def select_board(): + """ + select one board info + :return: the selected board info + """ + data = request.json if request.method == "POST" else request.args + board_info = data['board_info'] + current_app.config.update(BOARD_INFO=board_info) + + board_type = get_board_info_type(board_info) + current_app.config.update(BOARD_TYPE=board_type) + if board_type: + return board_type + + return '' + + +@CONFIG_APP.route('/upload_scenario', methods=['POST']) +def upload_scenario(): + """ + upload scenario setting xml file + :return: the upload status + """ + if request.method == 'POST': + if 'file' not in request.files: + return {'status': 'no file uploaded'} + file = request.files['file'] + if file and '.' in file.filename and file.filename.rsplit('.', 1)[1] in ['xml']: + filename = secure_filename(file.filename) + print(filename) + scenario_file_name = os.path.splitext(file.filename)[0] + board_type = current_app.config.get('BOARD_TYPE') + + tmp_scenario_name = 'tmp_' + scenario_file_name + '.xml' + tmp_scenario_file = os.path.join(current_app.config.get('CONFIG_PATH'), board_type, + 'user_defined', tmp_scenario_name) + if os.path.isfile(tmp_scenario_file): + os.remove(tmp_scenario_file) + + file.save(tmp_scenario_file) + + tmp_xml_config = XmlConfig(os.path.join(current_app.config.get('CONFIG_PATH'), + board_type, 'user_defined')) + tmp_xml_config.set_curr(tmp_scenario_name[:-4]) + status = None + if tmp_xml_config.get_curr_root() is None: + status = 'Error on parsing the scenario xml file, \n' \ + 'check the xml syntax and config items.' + else: + tmp_root = tmp_xml_config.get_curr_root() + if 'board' not in tmp_root.attrib or 'scenario' not in tmp_root.attrib \ + or 'uos_launcher' in tmp_root.attrib: + status = 'Invalid scenario xml file, \nonly board and scenario ' \ + 'need to be configured.' + elif tmp_root.attrib['board'] != current_app.config.get('BOARD_TYPE'): + status = 'Current board: {} mismatched with the board in the scenario file,' \ + '\nplease reselect or upload the board info: {}'\ + .format(current_app.config.get('BOARD_TYPE'), tmp_root.attrib['board']) + if status is not None: + if os.path.isfile(tmp_scenario_file): + os.remove(tmp_scenario_file) + return {'status': status} + + error_list = {} + new_scenario_name = scenario_file_name + rename = False + if not error_list: + new_scenario_path = os.path.join(current_app.config.get('CONFIG_PATH'), board_type, + 'user_defined', scenario_file_name + '.xml') + if os.path.isfile(new_scenario_path): + new_scenario_name = new_scenario_name + '_' \ + + datetime.now().strftime('%Y%m%d%H%M%S') + rename = True + + os.rename(tmp_scenario_file, os.path.join(current_app.config.get('CONFIG_PATH'), + board_type, 'user_defined', + new_scenario_name + '.xml')) + + return {'status': 'success', 'file_name': new_scenario_name, + 'rename': rename, 'error_list': error_list} + + return {'status': 'unsupported method'} + + +@CONFIG_APP.route('/upload_launch', methods=['POST']) +def upload_launch(): + """ + upload scenario setting xml file + :return: the upload status + """ + if request.method == 'POST': + if 'file' not in request.files: + return {'status': 'no file uploaded'} + file = request.files['file'] + if file and '.' in file.filename and file.filename.rsplit('.', 1)[1] in ['xml']: + filename = secure_filename(file.filename) + print(filename) + launch_file_name = os.path.splitext(file.filename)[0] + board_type = current_app.config.get('BOARD_TYPE') + + tmp_launch_name = 'tmp_' + launch_file_name + '.xml' + tmp_launch_file = os.path.join(current_app.config.get('CONFIG_PATH'), board_type, + 'user_defined', tmp_launch_name) + if os.path.isfile(tmp_launch_file): + os.remove(tmp_launch_file) + + file.save(tmp_launch_file) + + tmp_xml_config = XmlConfig(os.path.join(current_app.config.get('CONFIG_PATH'), + board_type, 'user_defined')) + tmp_xml_config.set_curr(tmp_launch_name[:-4]) + status = None + if tmp_xml_config.get_curr_root() is None: + status = 'Error on parsing the scenario xml file, \n' \ + 'check the xml syntax and config items.' + else: + tmp_root = tmp_xml_config.get_curr_root() + if 'board' not in tmp_root.attrib or 'scenario' not in tmp_root.attrib \ + or 'uos_launcher' not in tmp_root.attrib: + status = 'Invalid launch xml file, \nboard, scenario,' \ + 'and uos_launcher need to be configured.' + elif tmp_root.attrib['board'] != current_app.config.get('BOARD_TYPE'): + status = 'Current board: {} mismatched with the board in the launch file,' \ + '\nplease reselect or upload the board info: {}' \ + .format(current_app.config.get('BOARD_TYPE'), tmp_root.attrib['board']) + if status is not None: + if os.path.isfile(tmp_launch_file): + os.remove(tmp_launch_file) + return {'status': status} + + error_list = {} + new_launch_name = launch_file_name + rename = False + if not error_list: + new_launch_path = os.path.join(current_app.config.get('CONFIG_PATH'), board_type, + 'user_defined', launch_file_name + '.xml') + if os.path.isfile(new_launch_path): + new_launch_name = new_launch_name + '_' + \ + datetime.now().strftime('%Y%m%d%H%M%S') + rename = True + + os.rename(tmp_launch_file, os.path.join(current_app.config.get('CONFIG_PATH'), + board_type, 'user_defined', + new_launch_name + '.xml')) + + return {'status': 'success', 'file_name': new_launch_name, + 'rename': rename, 'error_list': error_list} + + return {'status': 'unsupported method'} + + +def get_board_list(): + """ + get all available board info files + :return: the file list of board info + """ + res_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'res') + if not os.path.isdir(res_path): + os.makedirs(res_path) + board_info_list = [] + for file in os.listdir(res_path): + if os.path.isfile(os.path.join(res_path, file)) and \ + '.' in file and file.rsplit('.', 1)[1] in ['xml']: + board_info_list.append(file.rsplit('.', 1)[0]) + return board_info_list + + +def get_xml_configs(user_defined=False): + """ + get xml config related variables + :return: board_info, board_config, scenario_config, launch_config + """ + + config_path = None + board_info = current_app.config.get('BOARD_INFO') + + board_type = get_board_info_type(board_info) + if board_type is not None: + config_path = os.path.join(current_app.config.get('CONFIG_PATH'), board_type) + + if user_defined: + scenario_config = XmlConfig(config_path, False) + launch_config = XmlConfig(config_path, False) + else: + scenario_config = XmlConfig(config_path) + launch_config = XmlConfig(config_path) + + return board_info, board_type, scenario_config, launch_config + + +def get_board_info_type(board_info): + """ + get board info type + :param board_info: the board info file + :return: the board type + """ + board_type = None + if board_info is not None: + board_info_config = XmlConfig(os.path.join(os.path.dirname(os.path.abspath(__file__)), + 'res')) + board_info_config.set_curr(board_info) + board_info_root = board_info_config.get_curr_root() + if board_info_root and 'board' in board_info_root.attrib: + board_type = board_info_root.attrib['board'] + + return board_type