feat(semaphore): trigger Ansible tasks at booking start/end via Semaphore
- Background scheduler checks every 30s for bookings that need setup or teardown - Per-lab Semaphore template IDs stored on the labs table - Booking flags track which jobs have been triggered and their Semaphore job IDs - Immediate teardown triggered when an active booking is cancelled - Settings UI section for Semaphore API URL, token, and project ID - Lab template form fields for setup/teardown template IDs - BookingDetailsModal shows live Ansible job status with manual trigger buttons
This commit is contained in:
@ -8,7 +8,7 @@ import { LabTemplate, Device, TopologyLink } from '../types';
|
||||
import TopologyPanel from './TopologyPanel';
|
||||
import {
|
||||
Server, Plus, Edit3, Trash, User, MapPin,
|
||||
Layers, ChevronRight, Save, X, Check, Pencil
|
||||
Layers, ChevronRight, Save, X, Check, Pencil, Terminal,
|
||||
} from 'lucide-react';
|
||||
|
||||
interface LabTemplatesProps {
|
||||
@ -47,12 +47,16 @@ export default function LabTemplates({
|
||||
contactPerson: string;
|
||||
location: string;
|
||||
deviceIds: string[];
|
||||
semaphoreSetupTemplateId: string;
|
||||
semaphoreTeardownTemplateId: string;
|
||||
}>({
|
||||
name: '',
|
||||
description: '',
|
||||
contactPerson: '',
|
||||
location: '',
|
||||
deviceIds: []
|
||||
deviceIds: [],
|
||||
semaphoreSetupTemplateId: '',
|
||||
semaphoreTeardownTemplateId: '',
|
||||
});
|
||||
|
||||
// Calculate filtered devices associated with selected lab
|
||||
@ -68,7 +72,9 @@ export default function LabTemplates({
|
||||
description: '',
|
||||
contactPerson: '',
|
||||
location: '',
|
||||
deviceIds: []
|
||||
deviceIds: [],
|
||||
semaphoreSetupTemplateId: '',
|
||||
semaphoreTeardownTemplateId: '',
|
||||
});
|
||||
setIsEditing(true);
|
||||
};
|
||||
@ -82,7 +88,9 @@ export default function LabTemplates({
|
||||
description: lab.description,
|
||||
contactPerson: lab.contactPerson,
|
||||
location: lab.location,
|
||||
deviceIds: [...lab.deviceIds]
|
||||
deviceIds: [...lab.deviceIds],
|
||||
semaphoreSetupTemplateId: lab.semaphoreSetupTemplateId || '',
|
||||
semaphoreTeardownTemplateId: lab.semaphoreTeardownTemplateId || '',
|
||||
});
|
||||
setIsEditing(true);
|
||||
};
|
||||
@ -126,7 +134,9 @@ export default function LabTemplates({
|
||||
contactPerson: formData.contactPerson,
|
||||
location: formData.location,
|
||||
deviceIds: formData.deviceIds,
|
||||
topology: tempLinks
|
||||
topology: tempLinks,
|
||||
semaphoreSetupTemplateId: formData.semaphoreSetupTemplateId,
|
||||
semaphoreTeardownTemplateId: formData.semaphoreTeardownTemplateId,
|
||||
};
|
||||
|
||||
if (formMode === 'add') {
|
||||
@ -525,6 +535,39 @@ export default function LabTemplates({
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Ansible Semaphore Automation */}
|
||||
<div className="border-t border-slate-800 pt-3">
|
||||
<label className="block text-slate-300 font-bold mb-1.5 flex items-center gap-1.5">
|
||||
<Terminal className="w-3.5 h-3.5 text-orange-400" />
|
||||
3. Ansible Automation (optional)
|
||||
</label>
|
||||
<p className="text-[10px] text-slate-400 mb-2">Semaphore task template IDs to trigger at booking start and end. Leave empty to skip automation for this lab.</p>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div>
|
||||
<label className="block text-[10px] text-slate-400 mb-1">Setup Template ID</label>
|
||||
<input
|
||||
type="text"
|
||||
inputMode="numeric"
|
||||
value={formData.semaphoreSetupTemplateId}
|
||||
onChange={(e) => setFormData({ ...formData, semaphoreSetupTemplateId: e.target.value })}
|
||||
className="w-full bg-slate-950 text-slate-100 border border-slate-800 rounded p-2 font-mono text-xs focus:outline-none focus:border-orange-500/60"
|
||||
placeholder="e.g. 3"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-[10px] text-slate-400 mb-1">Teardown Template ID</label>
|
||||
<input
|
||||
type="text"
|
||||
inputMode="numeric"
|
||||
value={formData.semaphoreTeardownTemplateId}
|
||||
onChange={(e) => setFormData({ ...formData, semaphoreTeardownTemplateId: e.target.value })}
|
||||
className="w-full bg-slate-950 text-slate-100 border border-slate-800 rounded p-2 font-mono text-xs focus:outline-none focus:border-orange-500/60"
|
||||
placeholder="e.g. 4"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Form submit handlers */}
|
||||
<div className="pt-3 border-t border-slate-800 flex justify-end gap-2.5">
|
||||
<button
|
||||
|
||||
Reference in New Issue
Block a user