фронт для дискавери отображения
This commit is contained in:
parent
e355c12ffa
commit
23da2fab55
4 changed files with 85 additions and 6 deletions
|
|
@ -26,6 +26,7 @@ from app.schemas.object import (
|
|||
ObjectUpdate,
|
||||
SyncResult,
|
||||
DiscoveryStarted,
|
||||
DiscoveryStatus,
|
||||
)
|
||||
from app.services import crypto
|
||||
from app.services.database import AsyncSessionLocal
|
||||
|
|
@ -36,6 +37,22 @@ from app.services.sse import sse_manager, SSEEvent
|
|||
|
||||
router = APIRouter(prefix="/api/v1/objects", tags=["objects"])
|
||||
|
||||
_discovery_tasks: dict[int, asyncio.Task] = {}
|
||||
|
||||
|
||||
def _discovery_task_id(obj_id: int) -> str:
|
||||
return f"disc-{obj_id}"
|
||||
|
||||
|
||||
def _is_discovery_running(obj_id: int) -> bool:
|
||||
task = _discovery_tasks.get(obj_id)
|
||||
return task is not None and not task.done()
|
||||
|
||||
|
||||
def _forget_discovery_task(obj_id: int, task: asyncio.Task) -> None:
|
||||
if _discovery_tasks.get(obj_id) is task:
|
||||
_discovery_tasks.pop(obj_id, None)
|
||||
|
||||
|
||||
def _apply_credentials(obj: Object, db_password: str | None, ssh_password: str | None) -> None:
|
||||
if db_password is not None:
|
||||
|
|
@ -300,9 +317,24 @@ async def discover_devices(
|
|||
):
|
||||
"""SSH into each rack PC, parse caps config, discover network devices."""
|
||||
await _load_object(db, obj_id)
|
||||
task_id = f"disc-{obj_id}"
|
||||
asyncio.create_task(_run_discovery_background(obj_id, task_id))
|
||||
return DiscoveryStarted(task_id=task_id)
|
||||
task_id = _discovery_task_id(obj_id)
|
||||
if _is_discovery_running(obj_id):
|
||||
return DiscoveryStarted(task_id=task_id, running=True, already_running=True)
|
||||
|
||||
task = asyncio.create_task(_run_discovery_background(obj_id, task_id))
|
||||
_discovery_tasks[obj_id] = task
|
||||
task.add_done_callback(lambda done_task: _forget_discovery_task(obj_id, done_task))
|
||||
return DiscoveryStarted(task_id=task_id, running=True, already_running=False)
|
||||
|
||||
|
||||
@router.get("/{obj_id}/discover/status", response_model=DiscoveryStatus)
|
||||
async def discovery_status(
|
||||
obj_id: int,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
_: User = Depends(get_current_user),
|
||||
):
|
||||
await _load_object(db, obj_id)
|
||||
return DiscoveryStatus(task_id=_discovery_task_id(obj_id), running=_is_discovery_running(obj_id))
|
||||
|
||||
|
||||
@router.post("/import/preview", response_model=ObjectImportPreview)
|
||||
|
|
|
|||
|
|
@ -145,3 +145,10 @@ class SyncResult(BaseModel):
|
|||
|
||||
class DiscoveryStarted(BaseModel):
|
||||
task_id: str
|
||||
running: bool = True
|
||||
already_running: bool = False
|
||||
|
||||
|
||||
class DiscoveryStatus(BaseModel):
|
||||
task_id: str
|
||||
running: bool
|
||||
|
|
|
|||
|
|
@ -250,6 +250,13 @@ export const useSyncObject = (objId: number) => {
|
|||
|
||||
export interface DiscoveryStarted {
|
||||
task_id: string
|
||||
running: boolean
|
||||
already_running: boolean
|
||||
}
|
||||
|
||||
export interface DiscoveryStatus {
|
||||
task_id: string
|
||||
running: boolean
|
||||
}
|
||||
|
||||
export const useDiscoverObject = (objId: number) =>
|
||||
|
|
@ -258,6 +265,15 @@ export const useDiscoverObject = (objId: number) =>
|
|||
api.post<DiscoveryStarted>(`/api/v1/objects/${objId}/discover`).then((r) => r.data),
|
||||
})
|
||||
|
||||
export const useDiscoveryStatus = (objId: number) =>
|
||||
useQuery({
|
||||
queryKey: ['discovery-status', objId],
|
||||
queryFn: () =>
|
||||
api.get<DiscoveryStatus>(`/api/v1/objects/${objId}/discover/status`).then((r) => r.data),
|
||||
enabled: !!objId,
|
||||
refetchInterval: (query) => (query.state.data?.running ? 2_000 : false),
|
||||
})
|
||||
|
||||
export interface SheetsSyncPayload {
|
||||
spreadsheet_id: string
|
||||
sheet_name?: string
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ import {
|
|||
useDeleteDevice,
|
||||
useDeleteAllDevices,
|
||||
useDiscoverObject,
|
||||
useDiscoveryStatus,
|
||||
useDevices,
|
||||
useObject,
|
||||
type DeviceItem,
|
||||
|
|
@ -148,6 +149,7 @@ export function ObjectDetailPage({ defaultMode = 'inventory' }: { defaultMode?:
|
|||
const { data: actions } = useActions()
|
||||
|
||||
const discoverMutation = useDiscoverObject(objId)
|
||||
const discoveryStatus = useDiscoveryStatus(objId)
|
||||
const deleteDevice = useDeleteDevice(objId)
|
||||
const createRack = useCreateRack(objId)
|
||||
const updateRack = useUpdateRack(objId)
|
||||
|
|
@ -173,12 +175,31 @@ export function ObjectDetailPage({ defaultMode = 'inventory' }: { defaultMode?:
|
|||
const [editRackForm] = Form.useForm()
|
||||
|
||||
const currentAction = actions?.find((a) => a.name === selectedAction)
|
||||
const discoveryRunning = discoverMutation.isPending || !!discoveryTaskId || !!discoveryStatus.data?.running
|
||||
|
||||
useEffect(() => {
|
||||
if (discoveryStatus.data?.running) {
|
||||
setDiscoveryTaskId(discoveryStatus.data.task_id)
|
||||
} else if (discoveryStatus.data && discoveryTaskId === discoveryStatus.data.task_id) {
|
||||
setDiscoveryTaskId(null)
|
||||
}
|
||||
}, [discoveryStatus.data, discoveryTaskId])
|
||||
|
||||
const handleDiscover = async () => {
|
||||
if (discoveryRunning) {
|
||||
setDiscoveryTaskId(discoveryTaskId ?? discoveryStatus.data?.task_id ?? `disc-${objId}`)
|
||||
setDiscoveryOpen(true)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const { task_id } = await discoverMutation.mutateAsync()
|
||||
const { task_id, already_running } = await discoverMutation.mutateAsync()
|
||||
setDiscoveryTaskId(task_id)
|
||||
setDiscoveryOpen(true)
|
||||
queryClient.invalidateQueries({ queryKey: ['discovery-status', objId] })
|
||||
if (already_running) {
|
||||
message.info('SSH discovery уже выполняется')
|
||||
}
|
||||
} catch {
|
||||
message.error('Ошибка SSH discovery')
|
||||
}
|
||||
|
|
@ -186,6 +207,8 @@ export function ObjectDetailPage({ defaultMode = 'inventory' }: { defaultMode?:
|
|||
|
||||
const handleDiscoveryComplete = () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['devices', objId] })
|
||||
queryClient.invalidateQueries({ queryKey: ['discovery-status', objId] })
|
||||
setDiscoveryTaskId(null)
|
||||
}
|
||||
|
||||
const handleDeleteDevice = async (row: DeviceItem) => {
|
||||
|
|
@ -630,9 +653,10 @@ export function ObjectDetailPage({ defaultMode = 'inventory' }: { defaultMode?:
|
|||
<Col>
|
||||
{mode === 'inventory' ? (
|
||||
<Space wrap>
|
||||
<Button icon={<ApiOutlined />} loading={discoverMutation.isPending} onClick={handleDiscover}>
|
||||
SSH Discovery
|
||||
<Button icon={<ApiOutlined />} loading={discoveryRunning} onClick={handleDiscover}>
|
||||
{discoveryRunning ? 'SSH Discovery...' : 'SSH Discovery'}
|
||||
</Button>
|
||||
{discoveryRunning && <Tag color="processing">выполняется</Tag>}
|
||||
<Button icon={<PlusOutlined />} type="primary" onClick={() => { setEditDevice(null); setDeviceModal(true) }}>
|
||||
Добавить устройство
|
||||
</Button>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue