一个好用的Debug轮询任务框架

1.任务层

/*********************************************************************
 * @fn      Task_Debug
 *
 * @brief   Debug任务.
 *
 * @param   none
 *
 * @return  none
 */
void Task_Debug(void)
{
	/* 执行命令任务 */
	if(Command_Debug_Set_Function() == Command_OK)
	{
	
	}
}

2.命令层

/**  
 *  @brief debug控制命令
 */
typedef struct
{
	Ctrl_StatusTypeDef Ctrl;
	
	uint8_t Timer;
	
	uint8_t Timer_BackUps;
	
	void (* Command)(void);
	
} Command_Debug; 


/* Private define ------------------------------------------------------------*/
#define Command_Debug_Tx(pdata, len)		Driver_UART_DMA_Tx(pdata, len)


/* Private function prototypes -----------------------------------------------*/
static void Command_Debug_ADC_Val_String(void);
static void Command_Debug_ADC_Val_Hex(void);


/* Private variables ---------------------------------------------------------*/
static Command_Debug		Cmd_Debug[] = 
{
//	控制位				运行时间		计数时间		任务命令
	{Ctrl_Disable,		0,			10,			Command_Debug_ADC_Val_String},
	{Ctrl_Disable,		0,			10,			Command_Debug_ADC_Val_Hex},
};


/* Private user code ---------------------------------------------------------*/
/*********************************************************************
 * @fn      Command_Debug_Set_Function
 *
 * @brief   Debug功能命令设置函数.
 *
 * @param	ID:功能ID号.
 *
 * @return  Command_StatusTypeDef:状态标志位
 */
Command_StatusTypeDef Command_Debug_Set_Function(void)
{
	for(uint8_t i = 0; i < (sizeof(Cmd_Debug)/sizeof(Cmd_Debug[0])); i++)
	{
		if(Cmd_Debug[i].Ctrl == Ctrl_Enable)
		{
			if(++Cmd_Debug[i].Timer >= Cmd_Debug[i].Timer_BackUps)
			{
				Cmd_Debug[i].Command();
				Cmd_Debug[i].Timer = 0;
				return Command_OK;
			}
		}
		else
		{
			Cmd_Debug[i].Timer = 0;
		}			
	}
	return Command_ERROR;
}


/*********************************************************************
 * @fn      Command_Debug_Set_Function_Ctrl
 *
 * @brief   设置Debug功能命令控制.
 *
 * @param	ID:功能ID号.
 *
 * @param	Ctrl_StatusTypeDef:控制命令
 *
 * @return  none
 */
void Command_Debug_Set_Function_Ctrl(uint8_t ID, Ctrl_StatusTypeDef Ctrl)
{
	if(ID < (sizeof(Cmd_Debug)/sizeof(Cmd_Debug[0])))
	{
		Cmd_Debug[ID].Ctrl = Ctrl;
	}
}


/*********************************************************************
 * @fn      Command_Debug_Set_Function_Timer
 *
 * @brief   设置Debug功能命令输出时间.
 *
 * @param	ID:功能ID号.
 *
 * @param	time:控制时间
 *
 * @return  none
 */
void Command_Debug_Set_Function_Timer(uint8_t ID, uint8_t time)
{
	if(ID < (sizeof(Cmd_Debug)/sizeof(Cmd_Debug[0])))
	{
		if(Cmd_Debug[ID].Timer_BackUps != time)
		{
			Cmd_Debug[ID].Timer_BackUps = time;
            //将发送时间写入flash
			Driver_Write_DFlash_Data(DFlash_Debug_Read_AD, Cmd_Debug[ID].Timer_BackUps, Ctrl_Enable);
		}
	}
}


/*********************************************************************
 * @fn      Command_Debug_Init
 *
 * @brief   debug init.
 *
 * @param   none
 *
 * @return  none
 */
static void Command_Debug_Init(void)
{
	for(uint8_t i = 0; i < (sizeof(Cmd_Debug)/sizeof(Cmd_Debug[0])); i++)
	{
		Cmd_Debug[i].Timer_BackUps = Driver_Read_DFlash_Data(DFlash_Debug_Read_AD);
	}
}
__Function_AUTOInit_Command(Command_Debug_Init);


/*********************************************************************
 * @fn      Command_Debug_ADC_Val_String
 *
 * @brief   ADC读取数据 -- 字符串模式.
 *
 * @param   none
 *
 * @return  none
 */
static void Command_Debug_ADC_Val_String(void)
{
	Debug("{AD}%d, %d, %d, %d, %d, %d\r\n", \
		Task_Get_ADC_Val(Valve_AD_A11), Task_Get_ADC_Val(Valve_AD_B11), \
		Task_Get_ADC_Val(Valve_AD_A12), Task_Get_ADC_Val(Valve_AD_B12), \
		Task_Get_ADC_Val(Valve_AD_A13), Task_Get_ADC_Val(Valve_AD_B13));
}


/*********************************************************************
 * @fn      Command_Debug_ADC_Val_Hex
 *
 * @brief   ADC读取数据 -- hex模式.
 *
 * @param   none
 *
 * @return  none
 */
static void Command_Debug_ADC_Val_Hex(void)
{

}

<think>由于Ansible本身是一个基于命令行的自动化工具,要构建一个Web管理页面,我们需要使用Web框架来搭建一个前端界面,并通过后端调用Ansible的命令或API。这里我们选择使用Flask作为Web框架,因为它轻量且易于集成。 方案概述: 1. 使用Flask搭建Web服务器。 2. 前端页面使用HTML、CSS和JavaScript(可能用到AJAX)来构建用户界面,用于输入Ansible命令或选择Playbook等。 3. 后端Flask应用接收前端请求,调用Ansible模块(如ansible-runner、subprocess)来执行命令,并将结果返回给前端。 4. 考虑到安全性,需要对用户输入进行严格的验证,避免命令注入等安全问题。 注意:由于Ansible命令通常需要以特定用户(如root)权限执行,我们需要考虑如何安全地处理权限问题。这里我们可以使用sudo或者设置特定的运行用户,但要注意安全。 我们将实现一个简单的Web界面,包含以下功能: - 执行Ad-hoc命令(例如:ping模块检查主机连通性) - 执行Playbook(选择Playbook文件并执行) 由于在Web界面中直接执行命令存在安全风险,因此在实际生产环境中需要增加身份验证、授权、输入过滤等安全措施。本示例将重点放在基本功能的实现上,安全措施需要根据实际情况加强。 步骤: 1. 安装必要的Python包:Flask, ansible-runner(或者使用subprocess,但ansible-runner提供了更好的接口) 2. 创建Flask应用,设置路由。 3. 编写前端页面(这里为了简化,只提供基本界面,实际项目可使用更复杂的前端框架)。 由于代码较长,我们将分部分展示。 首先,安装依赖(在虚拟环境中): pip install flask ansible-runner 注意:ansible-runner是一个用于运行Ansible命令的Python库,它封装了Ansible的运行过程。 项目结构: ansible_web/ app.py # Flask主程序 templates/ index.html # 主页面 static/ # 静态文件(可选) playbooks/ # 存放Playbook的目录(需要提前创建) 代码实现: 1. app.py(Flask后端):</think>以下是一个使用 Python 和 Flask 实现的简易 Ansible Web 管理页面方案,包含核心功能和代码实现: ### 解决方案概述 使用 Flask + Bootstrap + Paramiko 实现一个轻量级 Web 界面,核心功能包括: - 主机清单管理 - Playbook 执行监控 - 实时任务日志 - 简易任务调度 ```python # app.py - 主程序 from flask import Flask, render_template, request, jsonify, session import os import subprocess import threading import json from datetime import datetime app = Flask(__name__) app.secret_key = 'your_secret_key_here' # 配置文件路径 ANSIBLE_HOSTS = 'ansible/hosts' PLAYBOOK_DIR = 'ansible/playbooks/' LOG_DIR = 'logs/' # 确保目录存在 os.makedirs(PLAYBOOK_DIR, exist_ok=True) os.makedirs(LOG_DIR, exist_ok=True) @app.route('/') def index(): """主控制面板""" # 获取主机清单 hosts = parse_hosts_file() # 获取可用 Playbooks playbooks = [f for f in os.listdir(PLAYBOOK_DIR) if f.endswith('.yml')] return render_template('index.html', hosts=hosts, playbooks=playbooks) def parse_hosts_file(): """解析 Ansible 主机文件""" hosts = {'ungrouped': []} current_group = None try: with open(ANSIBLE_HOSTS, 'r') as f: for line in f.readlines(): line = line.strip() if line.startswith('[') and line.endswith(']'): current_group = line[1:-1] hosts[current_group] = [] elif line and not line.startswith('#') and current_group: hosts[current_group].append(line) except FileNotFoundError: pass return hosts @app.route('/run_playbook', methods=['POST']) def run_playbook(): """执行 Playbook 的 API 端点""" data = request.json playbook = data.get('playbook') target = data.get('target') if not playbook or not target: return jsonify({'error': 'Missing parameters'}), 400 # 生成唯一任务 ID task_id = datetime.now().strftime("%Y%m%d%H%M%S") log_file = os.path.join(LOG_DIR, f'{task_id}.log') # 在后台线程中执行 Ansible thread = threading.Thread( target=execute_ansible, args=(playbook, target, log_file) ) thread.start() # 存储任务信息 session[task_id] = { 'status': 'running', 'start_time': datetime.now().isoformat(), 'log_file': log_file } return jsonify({'task_id': task_id}) def execute_ansible(playbook, target, log_file): """实际执行 Ansible 命令""" playbook_path = os.path.join(PLAYBOOK_DIR, playbook) cmd = f"ansible-playbook -i {ANSIBLE_HOSTS} {playbook_path} --limit {target}" try: with open(log_file, 'w') as log: process = subprocess.Popen( cmd.split(), stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True ) # 实时写入日志 for line in process.stdout: log.write(line) log.flush() process.wait() status = 'success' if process.returncode == 0 else 'failed' except Exception as e: status = 'error' with open(log_file, 'a') as log: log.write(f"Execution error: {str(e)}") # 更新任务状态 session[f'task_{task_id}'] = { **session.get(f'task_{task_id}', {}), 'status': status, 'end_time': datetime.now().isoformat() } @app.route('/task_status/<task_id>') def task_status(task_id): """获取任务状态和日志""" task_info = session.get(f'task_{task_id}', {}) if not task_info: return jsonify({'error': 'Task not found'}), 404 # 读取日志内容 log_content = "" if os.path.exists(task_info['log_file']): with open(task_info['log_file'], 'r') as f: log_content = f.read() return jsonify({ 'status': task_info.get('status', 'unknown'), 'log': log_content }) if __name__ == '__main__': app.run(host='0.0.0.0', port=5000, debug=True) ``` ```html <!-- templates/index.html --> <!DOCTYPE html> <html> <head> <title>Ansible Web Console</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"> </head> <body> <div class="container mt-4"> <h1 class="mb-4">Ansible Web Console</h1> <!-- 任务控制面板 --> <div class="card mb-4"> <div class="card-header">Execute Playbook</div> <div class="card-body"> <div class="row g-3"> <div class="col-md-5"> <select class="form-select" id="playbookSelect"> {% for playbook in playbooks %} <option value="{{ playbook }}">{{ playbook }}</option> {% endfor %} </select> </div> <div class="col-md-5"> <select class="form-select" id="targetSelect"> {% for group, hosts in hosts.items() %} <optgroup label="{{ group }}"> {% for host in hosts %} <option value="{{ host }}">{{ host }}</option> {% endfor %} </optgroup> {% endfor %} </select> </div> <div class="col-md-2"> <button class="btn btn-primary w-100" onclick="runPlaybook()">Execute</button> </div> </div> </div> </div> <!-- 任务日志显示 --> <div class="card"> <div class="card-header">Task Output</div> <div class="card-body"> <pre id="outputLog" style="height: 400px; overflow-y: scroll;"></pre> </div> </div> </div> <script> let currentTaskId = null; function runPlaybook() { const playbook = document.getElementById('playbookSelect').value; const target = document.getElementById('targetSelect').value; fetch('/run_playbook', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ playbook, target }) }) .then(res => res.json()) .then(data => { currentTaskId = data.task_id; monitorTask(); }); } function monitorTask() { if (!currentTaskId) return; fetch(`/task_status/${currentTaskId}`) .then(res => res.json()) .then(data => { const logElement = document.getElementById('outputLog'); logElement.textContent = data.log; logElement.scrollTop = logElement.scrollHeight; if (data.status === 'running') { setTimeout(monitorTask, 2000); } else { logElement.textContent += `\n\nTask ${data.status.toUpperCase()}!`; currentTaskId = null; } }); } </script> </body> </html> ``` ### 代码解释 1. **核心组件**: - **Flask**:Web 框架 - **Bootstrap**:前端界面 - **subprocess**:执行 Ansible 命令 - **threading**:后台任务处理 2. **关键功能**: - **主机清单解析**:解析 Ansible 的 hosts 文件 - **任务执行**:通过子进程运行 `ansible-playbook` - **日志管理**:实时捕获和存储任务输出 - **任务监控**:通过 AJAX 轮询任务状态 3. **安全注意事项**: - 使用会话存储任务状态 - 输入参数验证 - 错误处理机制 - 日志文件隔离 4. **目录结构**: ``` project/ ├── app.py ├── templates/ │ └── index.html ├── ansible/ │ ├── hosts # Ansible 主机清单 │ └── playbooks/ # 存储 Playbook 文件 └── logs/ # 任务日志存储 ``` ### 使用说明 1. 创建虚拟环境:`python -m venv venv` 2. 安装依赖:`pip install flask` 3. 准备 Ansible 环境(确保已安装) 4. 配置主机清单文件 (`ansible/hosts`) 5. 将 Playbook 放入 `ansible/playbooks/` 6. 启动应用:`python app.py` 访问 `http://localhost:5000` 即可使用 ### 功能扩展建议 - 添加用户认证系统 - 实现任务历史查看 - 增加 Playbook 编辑器 - 添加邮件通知功能 - 实现定时任务调度
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值