CMDB 자동 발견 (4개): - autodiscovery.py: SSH 네트워크 스캔 + CMDB 자동 등록 - snmp_discovery.py: SNMP v2c/v3 장비 자동 발견 - dependency_map.py: 서비스 의존성 자동 매핑 (netstat) - config_inventory.py: 서버 인벤토리 자동 수집 (SSH) NL 쿼리 엔진 (3개): - nlquery.py: Text-to-SQL (SELECT 전용, DML 차단) - op_assistant.py: Multi-turn 대화형 운영 어시스턴트 - query_history.py: 쿼리 이력·즐겨찾기·공유 구성 드리프트 (3개): - drift_detection.py: 골든 구성 vs 실제 비교·SR 자동 생성 - golden_config.py: 내장 CSAP 템플릿 + 버전 관리 - auto_remediation.py: 승인 기반 자동 교정 + 롤백 멀티클라우드 (4개): - multicloud.py: 통합 관제 (NCloud+AWS+KT) - aws_connector.py: AWS SigV4 직접 서명 연동 - cost_optimizer.py: AI 비용 최적화 권고 - cloud_migration.py: On-prem→K-Cloud 체크리스트 공공기관 특화 (6개): - narasajang.py: 나라장터 OpenAPI 연동 - public_api_hub.py: data.go.kr KISA·기상청 허브 - isp_support.py: ISP 수립 지원 + AI 보고서 - network_zone.py: 행정망/인터넷망 분리 관리 - k_cloud.py: 정부 K-Cloud 전환 자동화 - e_procurement.py: 전자조달 계약·검수·납품 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
118 lines
5.1 KiB
Python
118 lines
5.1 KiB
Python
"""
|
|
멀티클라우드 통합 관제 — NCloud + AWS + 기타
|
|
|
|
엔드포인트:
|
|
GET /api/multicloud/providers — 등록된 프로바이더 목록
|
|
POST /api/multicloud/providers — 프로바이더 등록
|
|
DELETE /api/multicloud/providers/{id}— 프로바이더 삭제
|
|
GET /api/multicloud/resources — 전체 클라우드 리소스 통합
|
|
GET /api/multicloud/costs — 전체 비용 통합
|
|
GET /api/multicloud/summary — 멀티클라우드 현황 요약
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import json, logging
|
|
from datetime import datetime
|
|
from typing import Optional
|
|
|
|
import httpx
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
from pydantic import BaseModel, Field
|
|
from sqlalchemy import select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from core.auth import get_current_user, require_admin_role
|
|
from database import get_db
|
|
from models import User, MultiCloudConfig
|
|
|
|
logger = logging.getLogger(__name__)
|
|
router = APIRouter(prefix="/api/multicloud", tags=["멀티클라우드"])
|
|
|
|
SUPPORTED_PROVIDERS = ["ncloud", "aws", "gcp", "azure", "kt_cloud", "naver_cloud"]
|
|
|
|
|
|
class ProviderCreate(BaseModel):
|
|
name: str
|
|
provider_type: str = Field(..., description="ncloud|aws|gcp|azure|kt_cloud")
|
|
region: str = "kr-1"
|
|
access_key: str
|
|
secret_key: str
|
|
extra_config: Optional[dict] = None
|
|
|
|
|
|
@router.post("/providers")
|
|
async def add_provider(req: ProviderCreate, db: AsyncSession = Depends(get_db), user: User = Depends(require_admin_role)):
|
|
if req.provider_type not in SUPPORTED_PROVIDERS:
|
|
raise HTTPException(400, f"미지원 프로바이더: {req.provider_type}")
|
|
cfg = MultiCloudConfig(
|
|
tenant_id=user.tenant_id, name=req.name,
|
|
provider_type=req.provider_type, region=req.region,
|
|
access_key=req.access_key,
|
|
secret_key_enc=req.secret_key, # TODO: AES-256-GCM
|
|
extra_config=json.dumps(req.extra_config or {}),
|
|
is_active=True, created_at=datetime.utcnow(),
|
|
)
|
|
db.add(cfg); await db.commit(); await db.refresh(cfg)
|
|
return {"ok": True, "id": cfg.id}
|
|
|
|
|
|
@router.get("/providers")
|
|
async def list_providers(db: AsyncSession = Depends(get_db), user: User = Depends(get_current_user)):
|
|
rows = await db.execute(select(MultiCloudConfig).where(MultiCloudConfig.tenant_id == user.tenant_id, MultiCloudConfig.is_active == True))
|
|
return [{"id": c.id, "name": c.name, "type": c.provider_type, "region": c.region} for c in rows.scalars().all()]
|
|
|
|
|
|
@router.delete("/providers/{config_id}")
|
|
async def delete_provider(config_id: int, db: AsyncSession = Depends(get_db), user: User = Depends(require_admin_role)):
|
|
row = await db.execute(select(MultiCloudConfig).where(MultiCloudConfig.id == config_id, MultiCloudConfig.tenant_id == user.tenant_id))
|
|
cfg = row.scalar_one_or_none()
|
|
if not cfg: raise HTTPException(404)
|
|
cfg.is_active = False; await db.commit()
|
|
return {"ok": True}
|
|
|
|
|
|
@router.get("/resources")
|
|
async def list_all_resources(db: AsyncSession = Depends(get_db), user: User = Depends(get_current_user)):
|
|
rows = await db.execute(select(MultiCloudConfig).where(MultiCloudConfig.tenant_id == user.tenant_id, MultiCloudConfig.is_active == True))
|
|
cfgs = rows.scalars().all()
|
|
all_resources = []
|
|
for cfg in cfgs:
|
|
# NCloud 경유 실제 조회 (다른 프로바이더는 설정 완료 후 활성화)
|
|
if cfg.provider_type in ("ncloud", "naver_cloud"):
|
|
try:
|
|
from routers.ncloud import list_servers as ncloud_list
|
|
# 기존 ncloud.py 재활용
|
|
resources = await _get_ncloud_resources(cfg)
|
|
for r in resources:
|
|
r["provider"] = cfg.name
|
|
r["provider_type"] = cfg.provider_type
|
|
all_resources.extend(resources)
|
|
except Exception as e:
|
|
logger.warning(f"NCloud 조회 실패: {e}")
|
|
else:
|
|
all_resources.append({"provider": cfg.name, "provider_type": cfg.provider_type, "note": "설정 후 활성화"})
|
|
return {"resources": all_resources, "total": len(all_resources)}
|
|
|
|
|
|
async def _get_ncloud_resources(cfg: MultiCloudConfig) -> list[dict]:
|
|
try:
|
|
from routers.ncloud import _ncloud_request
|
|
data = await _ncloud_request(cfg, "GET", "/vserver/v2/getServerInstanceList")
|
|
if data:
|
|
return [{"id": s.get("serverInstanceNo"), "name": s.get("serverName"),
|
|
"status": s.get("serverInstanceStatus", {}).get("codeName"),
|
|
"ip": s.get("publicIp")} for s in data.get("getServerInstanceListResponse", {}).get("serverInstanceList", [])]
|
|
except Exception: pass
|
|
return []
|
|
|
|
|
|
@router.get("/summary")
|
|
async def multicloud_summary(db: AsyncSession = Depends(get_db), user: User = Depends(get_current_user)):
|
|
rows = await db.execute(select(MultiCloudConfig).where(MultiCloudConfig.tenant_id == user.tenant_id, MultiCloudConfig.is_active == True))
|
|
cfgs = rows.scalars().all()
|
|
return {
|
|
"provider_count": len(cfgs),
|
|
"providers": [{"name": c.name, "type": c.provider_type} for c in cfgs],
|
|
"last_checked": datetime.utcnow(),
|
|
}
|