Skip to content

Commit a9ee09d

Browse files
committed
[UPD] updated
1 parent 49eb377 commit a9ee09d

File tree

3 files changed

+976
-96
lines changed

3 files changed

+976
-96
lines changed

dashboard/backend/app_v1_backup.py

Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
from fastapi import FastAPI, HTTPException, BackgroundTasks
2+
from fastapi.middleware.cors import CORSMiddleware
3+
from pydantic import BaseModel
4+
from typing import List, Dict, Optional, Any
5+
import os
6+
import yaml
7+
import configparser
8+
import subprocess
9+
import asyncio
10+
from pathlib import Path
11+
import uuid
12+
from datetime import datetime
13+
14+
app = FastAPI(title="Ansible Dashboard")
15+
16+
app.add_middleware(
17+
CORSMiddleware,
18+
allow_origins=["*"],
19+
allow_credentials=True,
20+
allow_methods=["*"],
21+
allow_headers=["*"],
22+
)
23+
24+
# Store for running jobs
25+
jobs_store: Dict[str, Dict[str, Any]] = {}
26+
27+
# Base paths
28+
# Check if running in Docker (Ansible folder is mounted at /app/Ansible)
29+
if Path("/app/Ansible").exists():
30+
ANSIBLE_BASE = Path("/app/Ansible")
31+
else:
32+
ANSIBLE_BASE = Path(__file__).parent.parent.parent / "Ansible"
33+
34+
class InventoryEntry(BaseModel):
35+
name: str
36+
host: str
37+
user: str
38+
group: str
39+
40+
class AnsibleFolder(BaseModel):
41+
name: str
42+
path: str
43+
has_inventory: bool
44+
has_vars: bool
45+
has_playbooks: bool
46+
playbooks: List[str]
47+
48+
class PlaybookRequest(BaseModel):
49+
folder: str
50+
playbook: str
51+
inventory: str
52+
vars: Dict[str, Any]
53+
54+
class JobStatus(BaseModel):
55+
job_id: str
56+
status: str
57+
output: str
58+
started_at: str
59+
completed_at: Optional[str]
60+
61+
@app.get("/")
62+
async def root():
63+
return {"message": "Ansible Dashboard API"}
64+
65+
@app.get("/api/folders", response_model=List[AnsibleFolder])
66+
async def get_ansible_folders():
67+
"""Get all Ansible folders with their details"""
68+
folders = []
69+
70+
if not ANSIBLE_BASE.exists():
71+
return folders
72+
73+
for folder in ANSIBLE_BASE.iterdir():
74+
if folder.is_dir() and not folder.name.startswith('.'):
75+
playbooks = []
76+
has_inventory = False
77+
has_vars = False
78+
79+
# Check for inventory files
80+
if (folder / "inventory.ini").exists() or (folder / "hosts").exists():
81+
has_inventory = True
82+
83+
# Check for vars files
84+
if (folder / "vars.yml").exists() or (folder / "variables.yml").exists():
85+
has_vars = True
86+
87+
# Find playbook files
88+
for file in folder.glob("*.yml"):
89+
if file.name not in ["vars.yml", "variables.yml"]:
90+
playbooks.append(file.name)
91+
92+
folders.append(AnsibleFolder(
93+
name=folder.name,
94+
path=str(folder),
95+
has_inventory=has_inventory,
96+
has_vars=has_vars,
97+
has_playbooks=len(playbooks) > 0,
98+
playbooks=playbooks
99+
))
100+
101+
return sorted(folders, key=lambda x: x.name)
102+
103+
@app.get("/api/folders/{folder_name}/inventory")
104+
async def get_inventory(folder_name: str):
105+
"""Parse and return inventory file content"""
106+
folder_path = ANSIBLE_BASE / folder_name
107+
108+
inventory_file = folder_path / "inventory.ini"
109+
if not inventory_file.exists():
110+
inventory_file = folder_path / "hosts"
111+
112+
if not inventory_file.exists():
113+
raise HTTPException(status_code=404, detail="Inventory file not found")
114+
115+
config = configparser.ConfigParser(allow_no_value=True)
116+
config.read(inventory_file)
117+
118+
inventory_data = {}
119+
for section in config.sections():
120+
hosts = []
121+
for key in config[section]:
122+
# Parse inventory line
123+
parts = key.split()
124+
host_info = {"name": parts[0] if parts else key}
125+
126+
for part in parts[1:]:
127+
if "=" in part:
128+
k, v = part.split("=", 1)
129+
host_info[k] = v
130+
131+
hosts.append(host_info)
132+
inventory_data[section] = hosts
133+
134+
return {
135+
"content": inventory_data,
136+
"raw": inventory_file.read_text()
137+
}
138+
139+
@app.get("/api/folders/{folder_name}/vars")
140+
async def get_vars(folder_name: str):
141+
"""Parse and return vars file content"""
142+
folder_path = ANSIBLE_BASE / folder_name
143+
144+
vars_file = folder_path / "vars.yml"
145+
if not vars_file.exists():
146+
vars_file = folder_path / "variables.yml"
147+
148+
if not vars_file.exists():
149+
raise HTTPException(status_code=404, detail="Vars file not found")
150+
151+
with open(vars_file, 'r') as f:
152+
vars_data = yaml.safe_load(f) or {}
153+
154+
return {
155+
"content": vars_data,
156+
"raw": vars_file.read_text()
157+
}
158+
159+
@app.post("/api/folders/{folder_name}/inventory")
160+
async def update_inventory(folder_name: str, content: Dict[str, Any]):
161+
"""Update inventory file"""
162+
folder_path = ANSIBLE_BASE / folder_name
163+
inventory_file = folder_path / "inventory.ini"
164+
165+
if "raw" in content:
166+
# Save raw content
167+
inventory_file.write_text(content["raw"])
168+
else:
169+
# Generate from structured data
170+
lines = []
171+
for group, hosts in content.items():
172+
lines.append(f"[{group}]")
173+
for host in hosts:
174+
host_line = host.get("name", "")
175+
for key, value in host.items():
176+
if key != "name":
177+
host_line += f" {key}={value}"
178+
lines.append(host_line)
179+
lines.append("")
180+
181+
inventory_file.write_text("\n".join(lines))
182+
183+
return {"success": True}
184+
185+
@app.post("/api/folders/{folder_name}/vars")
186+
async def update_vars(folder_name: str, content: Dict[str, Any]):
187+
"""Update vars file"""
188+
folder_path = ANSIBLE_BASE / folder_name
189+
vars_file = folder_path / "vars.yml"
190+
191+
if "raw" in content:
192+
vars_file.write_text(content["raw"])
193+
else:
194+
with open(vars_file, 'w') as f:
195+
yaml.dump(content, f, default_flow_style=False)
196+
197+
return {"success": True}
198+
199+
async def run_ansible_playbook(job_id: str, folder: str, playbook: str, inventory: str):
200+
"""Run ansible playbook in background"""
201+
folder_path = ANSIBLE_BASE / folder
202+
playbook_path = folder_path / playbook
203+
inventory_path = folder_path / inventory
204+
205+
jobs_store[job_id]["status"] = "running"
206+
207+
try:
208+
# Change to the folder directory to respect ansible.cfg
209+
process = await asyncio.create_subprocess_exec(
210+
"ansible-playbook",
211+
"-i", str(inventory_path),
212+
str(playbook_path),
213+
cwd=str(folder_path),
214+
stdout=asyncio.subprocess.PIPE,
215+
stderr=asyncio.subprocess.STDOUT
216+
)
217+
218+
output, _ = await process.communicate()
219+
220+
jobs_store[job_id]["output"] = output.decode()
221+
jobs_store[job_id]["status"] = "completed" if process.returncode == 0 else "failed"
222+
jobs_store[job_id]["completed_at"] = datetime.now().isoformat()
223+
jobs_store[job_id]["return_code"] = process.returncode
224+
225+
except Exception as e:
226+
jobs_store[job_id]["status"] = "error"
227+
jobs_store[job_id]["output"] = str(e)
228+
jobs_store[job_id]["completed_at"] = datetime.now().isoformat()
229+
230+
@app.post("/api/run")
231+
async def run_playbook(request: PlaybookRequest, background_tasks: BackgroundTasks):
232+
"""Run ansible playbook"""
233+
job_id = str(uuid.uuid4())
234+
235+
jobs_store[job_id] = {
236+
"job_id": job_id,
237+
"status": "queued",
238+
"output": "",
239+
"started_at": datetime.now().isoformat(),
240+
"completed_at": None,
241+
"folder": request.folder,
242+
"playbook": request.playbook
243+
}
244+
245+
# Update vars if provided
246+
if request.vars:
247+
folder_path = ANSIBLE_BASE / request.folder
248+
vars_file = folder_path / "vars.yml"
249+
with open(vars_file, 'w') as f:
250+
yaml.dump(request.vars, f, default_flow_style=False)
251+
252+
background_tasks.add_task(
253+
run_ansible_playbook,
254+
job_id,
255+
request.folder,
256+
request.playbook,
257+
request.inventory
258+
)
259+
260+
return {"job_id": job_id}
261+
262+
@app.get("/api/jobs/{job_id}", response_model=JobStatus)
263+
async def get_job_status(job_id: str):
264+
"""Get job status"""
265+
if job_id not in jobs_store:
266+
raise HTTPException(status_code=404, detail="Job not found")
267+
268+
return jobs_store[job_id]
269+
270+
@app.get("/api/jobs")
271+
async def get_all_jobs():
272+
"""Get all jobs"""
273+
return list(jobs_store.values())
274+
275+
if __name__ == "__main__":
276+
import uvicorn
277+
uvicorn.run(app, host="0.0.0.0", port=8000)

0 commit comments

Comments
 (0)