v0.0.8 | CORS 설정에 X-NHN-Appkey 헤더 허용 추가
All checks were successful
Build And Test / build-and-push (push) Successful in 36s
All checks were successful
Build And Test / build-and-push (push) Successful in 36s
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
37
API_SPEC.md
37
API_SPEC.md
@ -411,7 +411,34 @@ GET /api/nhn/floatingip/
|
|||||||
|
|
||||||
## 4. NKS (Kubernetes) API
|
## 4. NKS (Kubernetes) API
|
||||||
|
|
||||||
### 4.1 클러스터 목록 조회
|
### 4.1 지원 버전 조회
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /api/nhn/nks/supports/
|
||||||
|
```
|
||||||
|
|
||||||
|
**Headers**
|
||||||
|
```
|
||||||
|
X-NHN-Region: kr2
|
||||||
|
X-NHN-Token: {token}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response (200 OK)**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"supported_k8s": {
|
||||||
|
"v1.33.4": "True",
|
||||||
|
"v1.32.4": "True",
|
||||||
|
"v1.31.4": "True",
|
||||||
|
"v1.30.4": "False"
|
||||||
|
},
|
||||||
|
"supported_event_type": {...}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4.2 클러스터 목록 조회
|
||||||
|
|
||||||
```
|
```
|
||||||
GET /api/nhn/nks/clusters/
|
GET /api/nhn/nks/clusters/
|
||||||
@ -439,7 +466,7 @@ X-NHN-Token: {token}
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 4.2 클러스터 상세 조회
|
### 4.3 클러스터 상세 조회
|
||||||
|
|
||||||
```
|
```
|
||||||
GET /api/nhn/nks/clusters/{cluster_name}/
|
GET /api/nhn/nks/clusters/{cluster_name}/
|
||||||
@ -447,7 +474,7 @@ GET /api/nhn/nks/clusters/{cluster_name}/
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 4.3 클러스터 kubeconfig 조회
|
### 4.4 클러스터 kubeconfig 조회
|
||||||
|
|
||||||
```
|
```
|
||||||
GET /api/nhn/nks/clusters/{cluster_name}/config/
|
GET /api/nhn/nks/clusters/{cluster_name}/config/
|
||||||
@ -462,7 +489,7 @@ GET /api/nhn/nks/clusters/{cluster_name}/config/
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 4.4 클러스터 생성
|
### 4.5 클러스터 생성
|
||||||
|
|
||||||
```
|
```
|
||||||
POST /api/nhn/nks/clusters/create/
|
POST /api/nhn/nks/clusters/create/
|
||||||
@ -505,7 +532,7 @@ POST /api/nhn/nks/clusters/create/
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 4.5 클러스터 삭제
|
### 4.6 클러스터 삭제
|
||||||
|
|
||||||
```
|
```
|
||||||
DELETE /api/nhn/nks/clusters/{cluster_name}/
|
DELETE /api/nhn/nks/clusters/{cluster_name}/
|
||||||
|
|||||||
@ -1,2 +1,3 @@
|
|||||||
# msa-django-nhn
|
# msa-django-nhn
|
||||||
|
|
||||||
|
python3 manage.py runserver 0.0.0.0:8900
|
||||||
@ -3,6 +3,7 @@ from .compute import ApiCompute
|
|||||||
from .vpc import ApiVpc
|
from .vpc import ApiVpc
|
||||||
from .nks import ApiNks
|
from .nks import ApiNks
|
||||||
from .storage import ApiStorageObject
|
from .storage import ApiStorageObject
|
||||||
|
from .dnsplus import ApiDnsPlus
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"NHNCloudToken",
|
"NHNCloudToken",
|
||||||
@ -10,4 +11,5 @@ __all__ = [
|
|||||||
"ApiVpc",
|
"ApiVpc",
|
||||||
"ApiNks",
|
"ApiNks",
|
||||||
"ApiStorageObject",
|
"ApiStorageObject",
|
||||||
|
"ApiDnsPlus",
|
||||||
]
|
]
|
||||||
|
|||||||
@ -46,6 +46,9 @@ class NHNCloudEndpoints:
|
|||||||
STORAGE_KR1 = "https://kr1-api-object-storage.nhncloudservice.com/v1"
|
STORAGE_KR1 = "https://kr1-api-object-storage.nhncloudservice.com/v1"
|
||||||
STORAGE_KR2 = "https://kr2-api-object-storage.nhncloudservice.com/v1"
|
STORAGE_KR2 = "https://kr2-api-object-storage.nhncloudservice.com/v1"
|
||||||
|
|
||||||
|
# DNS Plus (글로벌 서비스 - 리전 무관)
|
||||||
|
DNSPLUS = "https://dnsplus.api.nhncloudservice.com"
|
||||||
|
|
||||||
|
|
||||||
class NHNCloudAPIError(Exception):
|
class NHNCloudAPIError(Exception):
|
||||||
"""NHN Cloud API 에러"""
|
"""NHN Cloud API 에러"""
|
||||||
@ -163,3 +166,7 @@ class BaseAPI:
|
|||||||
def _delete(self, url: str, **kwargs) -> dict:
|
def _delete(self, url: str, **kwargs) -> dict:
|
||||||
"""DELETE 요청"""
|
"""DELETE 요청"""
|
||||||
return self._request("DELETE", url, **kwargs)
|
return self._request("DELETE", url, **kwargs)
|
||||||
|
|
||||||
|
def _patch(self, url: str, json_data: Optional[dict] = None, **kwargs) -> dict:
|
||||||
|
"""PATCH 요청"""
|
||||||
|
return self._request("PATCH", url, json_data=json_data, **kwargs)
|
||||||
|
|||||||
911
nhn/packages/dnsplus.py
Normal file
911
nhn/packages/dnsplus.py
Normal file
@ -0,0 +1,911 @@
|
|||||||
|
"""
|
||||||
|
NHN Cloud DNS Plus API Module
|
||||||
|
|
||||||
|
DNS Zone, 레코드 세트, GSLB, Pool, Health Check 관리
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from typing import Optional, List
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from .base import NHNCloudEndpoints, NHNCloudAPIError
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class ApiDnsPlus:
|
||||||
|
"""NHN Cloud DNS Plus API 클래스"""
|
||||||
|
|
||||||
|
DEFAULT_TIMEOUT = 30
|
||||||
|
|
||||||
|
def __init__(self, appkey: str):
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
appkey: NHN Cloud 앱키
|
||||||
|
"""
|
||||||
|
self.appkey = appkey
|
||||||
|
self.base_url = f"{NHNCloudEndpoints.DNSPLUS}/dnsplus/v1.0/appkeys/{appkey}"
|
||||||
|
self._session = requests.Session()
|
||||||
|
|
||||||
|
def _get_headers(self) -> dict:
|
||||||
|
"""DNS Plus API 헤더"""
|
||||||
|
return {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Accept": "application/json",
|
||||||
|
}
|
||||||
|
|
||||||
|
def _request(
|
||||||
|
self,
|
||||||
|
method: str,
|
||||||
|
url: str,
|
||||||
|
params: Optional[dict] = None,
|
||||||
|
json_data: Optional[dict] = None,
|
||||||
|
timeout: Optional[int] = None,
|
||||||
|
) -> dict:
|
||||||
|
"""HTTP 요청 실행"""
|
||||||
|
logger.info(f"[DnsPlus] 요청 시작 - method={method}, url={url}")
|
||||||
|
if params:
|
||||||
|
logger.info(f"[DnsPlus] 요청 파라미터 - params={params}")
|
||||||
|
if json_data:
|
||||||
|
logger.info(f"[DnsPlus] 요청 바디 - json={json_data}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = self._session.request(
|
||||||
|
method=method,
|
||||||
|
url=url,
|
||||||
|
params=params,
|
||||||
|
json=json_data,
|
||||||
|
headers=self._get_headers(),
|
||||||
|
timeout=timeout or self.DEFAULT_TIMEOUT,
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info(f"[DnsPlus] 응답 수신 - status_code={response.status_code}")
|
||||||
|
|
||||||
|
if response.text:
|
||||||
|
result = response.json()
|
||||||
|
# DNS Plus API는 header.isSuccessful로 성공 여부 판단
|
||||||
|
if "header" in result and not result["header"].get("isSuccessful", True):
|
||||||
|
error_msg = result["header"].get("resultMessage", "알 수 없는 오류")
|
||||||
|
error_code = result["header"].get("resultCode", response.status_code)
|
||||||
|
logger.error(f"[DnsPlus] API 오류 - code={error_code}, message={error_msg}")
|
||||||
|
raise NHNCloudAPIError(message=error_msg, code=error_code, details=result)
|
||||||
|
return result
|
||||||
|
return {}
|
||||||
|
|
||||||
|
except requests.exceptions.Timeout:
|
||||||
|
logger.error(f"[DnsPlus] 타임아웃 - url={url}")
|
||||||
|
raise NHNCloudAPIError("요청 시간이 초과되었습니다.", code=408)
|
||||||
|
except requests.exceptions.ConnectionError as e:
|
||||||
|
logger.error(f"[DnsPlus] 연결 오류 - url={url}, error={e}")
|
||||||
|
raise NHNCloudAPIError("서버에 연결할 수 없습니다.", code=503)
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
logger.error(f"[DnsPlus] 요청 오류 - url={url}, error={e}")
|
||||||
|
raise NHNCloudAPIError(f"요청 중 오류가 발생했습니다: {e}")
|
||||||
|
|
||||||
|
def _get(self, url: str, params: Optional[dict] = None) -> dict:
|
||||||
|
return self._request("GET", url, params=params)
|
||||||
|
|
||||||
|
def _post(self, url: str, json_data: Optional[dict] = None) -> dict:
|
||||||
|
return self._request("POST", url, json_data=json_data)
|
||||||
|
|
||||||
|
def _put(self, url: str, json_data: Optional[dict] = None) -> dict:
|
||||||
|
return self._request("PUT", url, json_data=json_data)
|
||||||
|
|
||||||
|
def _delete(self, url: str, params: Optional[dict] = None) -> dict:
|
||||||
|
return self._request("DELETE", url, params=params)
|
||||||
|
|
||||||
|
# ==================== DNS Zone ====================
|
||||||
|
|
||||||
|
def get_zone_list(
|
||||||
|
self,
|
||||||
|
zone_id_list: Optional[List[str]] = None,
|
||||||
|
zone_status_list: Optional[List[str]] = None,
|
||||||
|
search_zone_name: Optional[str] = None,
|
||||||
|
page: int = 1,
|
||||||
|
limit: int = 50,
|
||||||
|
) -> dict:
|
||||||
|
"""
|
||||||
|
DNS Zone 목록 조회
|
||||||
|
|
||||||
|
Args:
|
||||||
|
zone_id_list: Zone ID 목록
|
||||||
|
zone_status_list: Zone 상태 목록 (USE, DISUSE)
|
||||||
|
search_zone_name: 검색할 Zone 이름
|
||||||
|
page: 페이지 번호
|
||||||
|
limit: 페이지당 항목 수
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: Zone 목록
|
||||||
|
"""
|
||||||
|
url = f"{self.base_url}/zones"
|
||||||
|
params = {"page": page, "limit": limit}
|
||||||
|
|
||||||
|
if zone_id_list:
|
||||||
|
params["zoneIdList"] = ",".join(zone_id_list)
|
||||||
|
if zone_status_list:
|
||||||
|
params["zoneStatusList"] = ",".join(zone_status_list)
|
||||||
|
if search_zone_name:
|
||||||
|
params["searchZoneName"] = search_zone_name
|
||||||
|
|
||||||
|
return self._get(url, params)
|
||||||
|
|
||||||
|
def get_zone(self, zone_id: str) -> dict:
|
||||||
|
"""
|
||||||
|
DNS Zone 상세 조회
|
||||||
|
|
||||||
|
Args:
|
||||||
|
zone_id: Zone ID
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: Zone 상세 정보
|
||||||
|
"""
|
||||||
|
url = f"{self.base_url}/zones"
|
||||||
|
params = {"zoneIdList": zone_id}
|
||||||
|
result = self._get(url, params)
|
||||||
|
zones = result.get("zoneList", [])
|
||||||
|
if zones:
|
||||||
|
return {"zone": zones[0]}
|
||||||
|
raise NHNCloudAPIError(f"Zone을 찾을 수 없습니다: {zone_id}", code=404)
|
||||||
|
|
||||||
|
def create_zone(self, zone_name: str, description: Optional[str] = None) -> dict:
|
||||||
|
"""
|
||||||
|
DNS Zone 생성
|
||||||
|
|
||||||
|
Args:
|
||||||
|
zone_name: Zone 이름 (도메인, 예: example.com.)
|
||||||
|
description: 설명
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 생성된 Zone 정보
|
||||||
|
"""
|
||||||
|
url = f"{self.base_url}/zones"
|
||||||
|
payload = {
|
||||||
|
"zone": {
|
||||||
|
"zoneName": zone_name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if description:
|
||||||
|
payload["zone"]["description"] = description
|
||||||
|
|
||||||
|
logger.info(f"DNS Zone 생성 요청: {zone_name}")
|
||||||
|
return self._post(url, payload)
|
||||||
|
|
||||||
|
def update_zone(self, zone_id: str, description: str) -> dict:
|
||||||
|
"""
|
||||||
|
DNS Zone 수정
|
||||||
|
|
||||||
|
Args:
|
||||||
|
zone_id: Zone ID
|
||||||
|
description: 설명
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 수정된 Zone 정보
|
||||||
|
"""
|
||||||
|
url = f"{self.base_url}/zones/{zone_id}"
|
||||||
|
payload = {
|
||||||
|
"zone": {
|
||||||
|
"description": description,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info(f"DNS Zone 수정 요청: {zone_id}")
|
||||||
|
return self._put(url, payload)
|
||||||
|
|
||||||
|
def delete_zones(self, zone_id_list: List[str]) -> dict:
|
||||||
|
"""
|
||||||
|
DNS Zone 삭제 (비동기)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
zone_id_list: 삭제할 Zone ID 목록
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 삭제 결과
|
||||||
|
"""
|
||||||
|
url = f"{self.base_url}/zones/async"
|
||||||
|
params = {"zoneIdList": ",".join(zone_id_list)}
|
||||||
|
|
||||||
|
logger.info(f"DNS Zone 삭제 요청: {zone_id_list}")
|
||||||
|
return self._delete(url, params)
|
||||||
|
|
||||||
|
# ==================== Record Set ====================
|
||||||
|
|
||||||
|
def get_recordset_list(
|
||||||
|
self,
|
||||||
|
zone_id: str,
|
||||||
|
recordset_id_list: Optional[List[str]] = None,
|
||||||
|
recordset_type_list: Optional[List[str]] = None,
|
||||||
|
search_recordset_name: Optional[str] = None,
|
||||||
|
page: int = 1,
|
||||||
|
limit: int = 50,
|
||||||
|
) -> dict:
|
||||||
|
"""
|
||||||
|
레코드 세트 목록 조회
|
||||||
|
|
||||||
|
Args:
|
||||||
|
zone_id: Zone ID
|
||||||
|
recordset_id_list: 레코드 세트 ID 목록
|
||||||
|
recordset_type_list: 레코드 타입 목록 (A, AAAA, CNAME, MX, TXT, NS, SRV, etc.)
|
||||||
|
search_recordset_name: 검색할 레코드 세트 이름
|
||||||
|
page: 페이지 번호
|
||||||
|
limit: 페이지당 항목 수
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 레코드 세트 목록
|
||||||
|
"""
|
||||||
|
url = f"{self.base_url}/zones/{zone_id}/recordsets"
|
||||||
|
params = {"page": page, "limit": limit}
|
||||||
|
|
||||||
|
if recordset_id_list:
|
||||||
|
params["recordsetIdList"] = ",".join(recordset_id_list)
|
||||||
|
if recordset_type_list:
|
||||||
|
params["recordsetTypeList"] = ",".join(recordset_type_list)
|
||||||
|
if search_recordset_name:
|
||||||
|
params["searchRecordsetName"] = search_recordset_name
|
||||||
|
|
||||||
|
return self._get(url, params)
|
||||||
|
|
||||||
|
def get_recordset(self, zone_id: str, recordset_id: str) -> dict:
|
||||||
|
"""
|
||||||
|
레코드 세트 상세 조회
|
||||||
|
|
||||||
|
Args:
|
||||||
|
zone_id: Zone ID
|
||||||
|
recordset_id: 레코드 세트 ID
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 레코드 세트 상세 정보
|
||||||
|
"""
|
||||||
|
url = f"{self.base_url}/zones/{zone_id}/recordsets"
|
||||||
|
params = {"recordsetIdList": recordset_id}
|
||||||
|
result = self._get(url, params)
|
||||||
|
recordsets = result.get("recordsetList", [])
|
||||||
|
if recordsets:
|
||||||
|
return {"recordset": recordsets[0]}
|
||||||
|
raise NHNCloudAPIError(f"레코드 세트를 찾을 수 없습니다: {recordset_id}", code=404)
|
||||||
|
|
||||||
|
def create_recordset(
|
||||||
|
self,
|
||||||
|
zone_id: str,
|
||||||
|
recordset_name: str,
|
||||||
|
recordset_type: str,
|
||||||
|
recordset_ttl: int,
|
||||||
|
record_list: List[dict],
|
||||||
|
) -> dict:
|
||||||
|
"""
|
||||||
|
레코드 세트 생성
|
||||||
|
|
||||||
|
Args:
|
||||||
|
zone_id: Zone ID
|
||||||
|
recordset_name: 레코드 세트 이름
|
||||||
|
recordset_type: 레코드 타입 (A, AAAA, CNAME, MX, TXT, NS, SRV, etc.)
|
||||||
|
recordset_ttl: TTL (초)
|
||||||
|
record_list: 레코드 목록 [{"recordContent": "...", "recordDisabled": False}]
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 생성된 레코드 세트 정보
|
||||||
|
"""
|
||||||
|
url = f"{self.base_url}/zones/{zone_id}/recordsets"
|
||||||
|
payload = {
|
||||||
|
"recordset": {
|
||||||
|
"recordsetName": recordset_name,
|
||||||
|
"recordsetType": recordset_type,
|
||||||
|
"recordsetTtl": recordset_ttl,
|
||||||
|
"recordList": record_list,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info(f"레코드 세트 생성 요청: {recordset_name} ({recordset_type})")
|
||||||
|
return self._post(url, payload)
|
||||||
|
|
||||||
|
def create_recordset_bulk(
|
||||||
|
self,
|
||||||
|
zone_id: str,
|
||||||
|
recordset_list: List[dict],
|
||||||
|
) -> dict:
|
||||||
|
"""
|
||||||
|
레코드 세트 대량 생성
|
||||||
|
|
||||||
|
Args:
|
||||||
|
zone_id: Zone ID
|
||||||
|
recordset_list: 레코드 세트 목록 (최대 2,000개)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 생성된 레코드 세트 정보
|
||||||
|
"""
|
||||||
|
url = f"{self.base_url}/zones/{zone_id}/recordsets/list"
|
||||||
|
payload = {"recordsetList": recordset_list}
|
||||||
|
|
||||||
|
logger.info(f"레코드 세트 대량 생성 요청: {len(recordset_list)}개")
|
||||||
|
return self._post(url, payload)
|
||||||
|
|
||||||
|
def update_recordset(
|
||||||
|
self,
|
||||||
|
zone_id: str,
|
||||||
|
recordset_id: str,
|
||||||
|
recordset_type: str,
|
||||||
|
recordset_ttl: int,
|
||||||
|
record_list: List[dict],
|
||||||
|
) -> dict:
|
||||||
|
"""
|
||||||
|
레코드 세트 수정
|
||||||
|
|
||||||
|
Args:
|
||||||
|
zone_id: Zone ID
|
||||||
|
recordset_id: 레코드 세트 ID
|
||||||
|
recordset_type: 레코드 타입
|
||||||
|
recordset_ttl: TTL (초)
|
||||||
|
record_list: 레코드 목록
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 수정된 레코드 세트 정보
|
||||||
|
"""
|
||||||
|
url = f"{self.base_url}/zones/{zone_id}/recordsets/{recordset_id}"
|
||||||
|
payload = {
|
||||||
|
"recordset": {
|
||||||
|
"recordsetType": recordset_type,
|
||||||
|
"recordsetTtl": recordset_ttl,
|
||||||
|
"recordList": record_list,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info(f"레코드 세트 수정 요청: {recordset_id}")
|
||||||
|
return self._put(url, payload)
|
||||||
|
|
||||||
|
def delete_recordsets(self, zone_id: str, recordset_id_list: List[str]) -> dict:
|
||||||
|
"""
|
||||||
|
레코드 세트 삭제
|
||||||
|
|
||||||
|
Args:
|
||||||
|
zone_id: Zone ID
|
||||||
|
recordset_id_list: 삭제할 레코드 세트 ID 목록
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 삭제 결과
|
||||||
|
"""
|
||||||
|
url = f"{self.base_url}/zones/{zone_id}/recordsets"
|
||||||
|
params = {"recordsetIdList": ",".join(recordset_id_list)}
|
||||||
|
|
||||||
|
logger.info(f"레코드 세트 삭제 요청: {recordset_id_list}")
|
||||||
|
return self._delete(url, params)
|
||||||
|
|
||||||
|
# ==================== GSLB ====================
|
||||||
|
|
||||||
|
def get_gslb_list(
|
||||||
|
self,
|
||||||
|
gslb_id_list: Optional[List[str]] = None,
|
||||||
|
search_gslb_name: Optional[str] = None,
|
||||||
|
gslb_domain: Optional[str] = None,
|
||||||
|
page: int = 1,
|
||||||
|
limit: int = 50,
|
||||||
|
) -> dict:
|
||||||
|
"""
|
||||||
|
GSLB 목록 조회
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gslb_id_list: GSLB ID 목록
|
||||||
|
search_gslb_name: 검색할 GSLB 이름
|
||||||
|
gslb_domain: GSLB 도메인
|
||||||
|
page: 페이지 번호
|
||||||
|
limit: 페이지당 항목 수
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: GSLB 목록
|
||||||
|
"""
|
||||||
|
url = f"{self.base_url}/gslbs"
|
||||||
|
params = {"page": page, "limit": limit}
|
||||||
|
|
||||||
|
if gslb_id_list:
|
||||||
|
params["gslbIdList"] = ",".join(gslb_id_list)
|
||||||
|
if search_gslb_name:
|
||||||
|
params["searchGslbName"] = search_gslb_name
|
||||||
|
if gslb_domain:
|
||||||
|
params["gslbDomain"] = gslb_domain
|
||||||
|
|
||||||
|
return self._get(url, params)
|
||||||
|
|
||||||
|
def get_gslb(self, gslb_id: str) -> dict:
|
||||||
|
"""
|
||||||
|
GSLB 상세 조회
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gslb_id: GSLB ID
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: GSLB 상세 정보
|
||||||
|
"""
|
||||||
|
url = f"{self.base_url}/gslbs"
|
||||||
|
params = {"gslbIdList": gslb_id}
|
||||||
|
result = self._get(url, params)
|
||||||
|
gslbs = result.get("gslbList", [])
|
||||||
|
if gslbs:
|
||||||
|
return {"gslb": gslbs[0]}
|
||||||
|
raise NHNCloudAPIError(f"GSLB를 찾을 수 없습니다: {gslb_id}", code=404)
|
||||||
|
|
||||||
|
def create_gslb(
|
||||||
|
self,
|
||||||
|
gslb_name: str,
|
||||||
|
gslb_ttl: int,
|
||||||
|
gslb_routing_rule: str,
|
||||||
|
gslb_disabled: bool = False,
|
||||||
|
connected_pool_list: Optional[List[dict]] = None,
|
||||||
|
) -> dict:
|
||||||
|
"""
|
||||||
|
GSLB 생성
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gslb_name: GSLB 이름
|
||||||
|
gslb_ttl: TTL (초)
|
||||||
|
gslb_routing_rule: 라우팅 규칙 (FAILOVER, RANDOM, GEOLOCATION)
|
||||||
|
gslb_disabled: 비활성화 여부
|
||||||
|
connected_pool_list: 연결된 Pool 목록
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 생성된 GSLB 정보
|
||||||
|
"""
|
||||||
|
url = f"{self.base_url}/gslbs"
|
||||||
|
payload = {
|
||||||
|
"gslb": {
|
||||||
|
"gslbName": gslb_name,
|
||||||
|
"gslbTtl": gslb_ttl,
|
||||||
|
"gslbRoutingRule": gslb_routing_rule,
|
||||||
|
"gslbDisabled": gslb_disabled,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if connected_pool_list:
|
||||||
|
payload["gslb"]["connectedPoolList"] = connected_pool_list
|
||||||
|
|
||||||
|
logger.info(f"GSLB 생성 요청: {gslb_name}")
|
||||||
|
return self._post(url, payload)
|
||||||
|
|
||||||
|
def update_gslb(
|
||||||
|
self,
|
||||||
|
gslb_id: str,
|
||||||
|
gslb_name: Optional[str] = None,
|
||||||
|
gslb_ttl: Optional[int] = None,
|
||||||
|
gslb_routing_rule: Optional[str] = None,
|
||||||
|
gslb_disabled: Optional[bool] = None,
|
||||||
|
connected_pool_list: Optional[List[dict]] = None,
|
||||||
|
) -> dict:
|
||||||
|
"""
|
||||||
|
GSLB 수정
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gslb_id: GSLB ID
|
||||||
|
gslb_name: GSLB 이름
|
||||||
|
gslb_ttl: TTL (초)
|
||||||
|
gslb_routing_rule: 라우팅 규칙
|
||||||
|
gslb_disabled: 비활성화 여부
|
||||||
|
connected_pool_list: 연결된 Pool 목록
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 수정된 GSLB 정보
|
||||||
|
"""
|
||||||
|
url = f"{self.base_url}/gslbs/{gslb_id}"
|
||||||
|
gslb_data = {}
|
||||||
|
|
||||||
|
if gslb_name is not None:
|
||||||
|
gslb_data["gslbName"] = gslb_name
|
||||||
|
if gslb_ttl is not None:
|
||||||
|
gslb_data["gslbTtl"] = gslb_ttl
|
||||||
|
if gslb_routing_rule is not None:
|
||||||
|
gslb_data["gslbRoutingRule"] = gslb_routing_rule
|
||||||
|
if gslb_disabled is not None:
|
||||||
|
gslb_data["gslbDisabled"] = gslb_disabled
|
||||||
|
if connected_pool_list is not None:
|
||||||
|
gslb_data["connectedPoolList"] = connected_pool_list
|
||||||
|
|
||||||
|
payload = {"gslb": gslb_data}
|
||||||
|
|
||||||
|
logger.info(f"GSLB 수정 요청: {gslb_id}")
|
||||||
|
return self._put(url, payload)
|
||||||
|
|
||||||
|
def delete_gslbs(self, gslb_id_list: List[str]) -> dict:
|
||||||
|
"""
|
||||||
|
GSLB 삭제
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gslb_id_list: 삭제할 GSLB ID 목록
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 삭제 결과
|
||||||
|
"""
|
||||||
|
url = f"{self.base_url}/gslbs"
|
||||||
|
params = {"gslbIdList": ",".join(gslb_id_list)}
|
||||||
|
|
||||||
|
logger.info(f"GSLB 삭제 요청: {gslb_id_list}")
|
||||||
|
return self._delete(url, params)
|
||||||
|
|
||||||
|
def connect_pool(
|
||||||
|
self,
|
||||||
|
gslb_id: str,
|
||||||
|
pool_id: str,
|
||||||
|
connected_pool_order: int,
|
||||||
|
connected_pool_region_content: Optional[str] = None,
|
||||||
|
) -> dict:
|
||||||
|
"""
|
||||||
|
GSLB에 Pool 연결
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gslb_id: GSLB ID
|
||||||
|
pool_id: Pool ID
|
||||||
|
connected_pool_order: 연결 순서 (우선순위)
|
||||||
|
connected_pool_region_content: 지역 콘텐츠 (GEOLOCATION 규칙 사용 시)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 연결 결과
|
||||||
|
"""
|
||||||
|
url = f"{self.base_url}/gslbs/{gslb_id}/connected-pools/{pool_id}"
|
||||||
|
payload = {
|
||||||
|
"connectedPool": {
|
||||||
|
"connectedPoolOrder": connected_pool_order,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if connected_pool_region_content:
|
||||||
|
payload["connectedPool"]["connectedPoolRegionContent"] = connected_pool_region_content
|
||||||
|
|
||||||
|
logger.info(f"Pool 연결 요청: GSLB={gslb_id}, Pool={pool_id}")
|
||||||
|
return self._post(url, payload)
|
||||||
|
|
||||||
|
def update_connected_pool(
|
||||||
|
self,
|
||||||
|
gslb_id: str,
|
||||||
|
pool_id: str,
|
||||||
|
connected_pool_order: int,
|
||||||
|
connected_pool_region_content: Optional[str] = None,
|
||||||
|
) -> dict:
|
||||||
|
"""
|
||||||
|
연결된 Pool 수정
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gslb_id: GSLB ID
|
||||||
|
pool_id: Pool ID
|
||||||
|
connected_pool_order: 연결 순서 (우선순위)
|
||||||
|
connected_pool_region_content: 지역 콘텐츠
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 수정 결과
|
||||||
|
"""
|
||||||
|
url = f"{self.base_url}/gslbs/{gslb_id}/connected-pools/{pool_id}"
|
||||||
|
payload = {
|
||||||
|
"connectedPool": {
|
||||||
|
"connectedPoolOrder": connected_pool_order,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if connected_pool_region_content:
|
||||||
|
payload["connectedPool"]["connectedPoolRegionContent"] = connected_pool_region_content
|
||||||
|
|
||||||
|
logger.info(f"연결된 Pool 수정 요청: GSLB={gslb_id}, Pool={pool_id}")
|
||||||
|
return self._put(url, payload)
|
||||||
|
|
||||||
|
def disconnect_pools(self, gslb_id: str, pool_id_list: List[str]) -> dict:
|
||||||
|
"""
|
||||||
|
GSLB에서 Pool 연결 해제
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gslb_id: GSLB ID
|
||||||
|
pool_id_list: 연결 해제할 Pool ID 목록
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 연결 해제 결과
|
||||||
|
"""
|
||||||
|
url = f"{self.base_url}/gslbs/{gslb_id}/connected-pools"
|
||||||
|
params = {"poolIdList": ",".join(pool_id_list)}
|
||||||
|
|
||||||
|
logger.info(f"Pool 연결 해제 요청: GSLB={gslb_id}, Pools={pool_id_list}")
|
||||||
|
return self._delete(url, params)
|
||||||
|
|
||||||
|
# ==================== Pool ====================
|
||||||
|
|
||||||
|
def get_pool_list(
|
||||||
|
self,
|
||||||
|
pool_id_list: Optional[List[str]] = None,
|
||||||
|
search_pool_name: Optional[str] = None,
|
||||||
|
health_check_id: Optional[str] = None,
|
||||||
|
page: int = 1,
|
||||||
|
limit: int = 50,
|
||||||
|
) -> dict:
|
||||||
|
"""
|
||||||
|
Pool 목록 조회
|
||||||
|
|
||||||
|
Args:
|
||||||
|
pool_id_list: Pool ID 목록
|
||||||
|
search_pool_name: 검색할 Pool 이름
|
||||||
|
health_check_id: Health Check ID
|
||||||
|
page: 페이지 번호
|
||||||
|
limit: 페이지당 항목 수
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: Pool 목록
|
||||||
|
"""
|
||||||
|
url = f"{self.base_url}/pools"
|
||||||
|
params = {"page": page, "limit": limit}
|
||||||
|
|
||||||
|
if pool_id_list:
|
||||||
|
params["poolIdList"] = ",".join(pool_id_list)
|
||||||
|
if search_pool_name:
|
||||||
|
params["searchPoolName"] = search_pool_name
|
||||||
|
if health_check_id:
|
||||||
|
params["healthCheckId"] = health_check_id
|
||||||
|
|
||||||
|
return self._get(url, params)
|
||||||
|
|
||||||
|
def get_pool(self, pool_id: str) -> dict:
|
||||||
|
"""
|
||||||
|
Pool 상세 조회
|
||||||
|
|
||||||
|
Args:
|
||||||
|
pool_id: Pool ID
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: Pool 상세 정보
|
||||||
|
"""
|
||||||
|
url = f"{self.base_url}/pools"
|
||||||
|
params = {"poolIdList": pool_id}
|
||||||
|
result = self._get(url, params)
|
||||||
|
pools = result.get("poolList", [])
|
||||||
|
if pools:
|
||||||
|
return {"pool": pools[0]}
|
||||||
|
raise NHNCloudAPIError(f"Pool을 찾을 수 없습니다: {pool_id}", code=404)
|
||||||
|
|
||||||
|
def create_pool(
|
||||||
|
self,
|
||||||
|
pool_name: str,
|
||||||
|
endpoint_list: List[dict],
|
||||||
|
pool_disabled: bool = False,
|
||||||
|
health_check_id: Optional[str] = None,
|
||||||
|
) -> dict:
|
||||||
|
"""
|
||||||
|
Pool 생성
|
||||||
|
|
||||||
|
Args:
|
||||||
|
pool_name: Pool 이름
|
||||||
|
endpoint_list: 엔드포인트 목록
|
||||||
|
[{"endpointAddress": "1.1.1.1", "endpointWeight": 1.0, "endpointDisabled": False}]
|
||||||
|
pool_disabled: 비활성화 여부
|
||||||
|
health_check_id: Health Check ID
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 생성된 Pool 정보
|
||||||
|
"""
|
||||||
|
url = f"{self.base_url}/pools"
|
||||||
|
payload = {
|
||||||
|
"pool": {
|
||||||
|
"poolName": pool_name,
|
||||||
|
"poolDisabled": pool_disabled,
|
||||||
|
"endpointList": endpoint_list,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if health_check_id:
|
||||||
|
payload["pool"]["healthCheckId"] = health_check_id
|
||||||
|
|
||||||
|
logger.info(f"Pool 생성 요청: {pool_name}")
|
||||||
|
return self._post(url, payload)
|
||||||
|
|
||||||
|
def update_pool(
|
||||||
|
self,
|
||||||
|
pool_id: str,
|
||||||
|
pool_name: Optional[str] = None,
|
||||||
|
endpoint_list: Optional[List[dict]] = None,
|
||||||
|
pool_disabled: Optional[bool] = None,
|
||||||
|
health_check_id: Optional[str] = None,
|
||||||
|
) -> dict:
|
||||||
|
"""
|
||||||
|
Pool 수정
|
||||||
|
|
||||||
|
Args:
|
||||||
|
pool_id: Pool ID
|
||||||
|
pool_name: Pool 이름
|
||||||
|
endpoint_list: 엔드포인트 목록
|
||||||
|
pool_disabled: 비활성화 여부
|
||||||
|
health_check_id: Health Check ID
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 수정된 Pool 정보
|
||||||
|
"""
|
||||||
|
url = f"{self.base_url}/pools/{pool_id}"
|
||||||
|
pool_data = {}
|
||||||
|
|
||||||
|
if pool_name is not None:
|
||||||
|
pool_data["poolName"] = pool_name
|
||||||
|
if endpoint_list is not None:
|
||||||
|
pool_data["endpointList"] = endpoint_list
|
||||||
|
if pool_disabled is not None:
|
||||||
|
pool_data["poolDisabled"] = pool_disabled
|
||||||
|
if health_check_id is not None:
|
||||||
|
pool_data["healthCheckId"] = health_check_id
|
||||||
|
|
||||||
|
payload = {"pool": pool_data}
|
||||||
|
|
||||||
|
logger.info(f"Pool 수정 요청: {pool_id}")
|
||||||
|
return self._put(url, payload)
|
||||||
|
|
||||||
|
def delete_pools(self, pool_id_list: List[str]) -> dict:
|
||||||
|
"""
|
||||||
|
Pool 삭제
|
||||||
|
|
||||||
|
Args:
|
||||||
|
pool_id_list: 삭제할 Pool ID 목록
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 삭제 결과
|
||||||
|
"""
|
||||||
|
url = f"{self.base_url}/pools"
|
||||||
|
params = {"poolIdList": ",".join(pool_id_list)}
|
||||||
|
|
||||||
|
logger.info(f"Pool 삭제 요청: {pool_id_list}")
|
||||||
|
return self._delete(url, params)
|
||||||
|
|
||||||
|
# ==================== Health Check ====================
|
||||||
|
|
||||||
|
def get_health_check_list(
|
||||||
|
self,
|
||||||
|
health_check_id_list: Optional[List[str]] = None,
|
||||||
|
search_health_check_name: Optional[str] = None,
|
||||||
|
page: int = 1,
|
||||||
|
limit: int = 50,
|
||||||
|
) -> dict:
|
||||||
|
"""
|
||||||
|
Health Check 목록 조회
|
||||||
|
|
||||||
|
Args:
|
||||||
|
health_check_id_list: Health Check ID 목록
|
||||||
|
search_health_check_name: 검색할 Health Check 이름
|
||||||
|
page: 페이지 번호
|
||||||
|
limit: 페이지당 항목 수
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: Health Check 목록
|
||||||
|
"""
|
||||||
|
url = f"{self.base_url}/health-checks"
|
||||||
|
params = {"page": page, "limit": limit}
|
||||||
|
|
||||||
|
if health_check_id_list:
|
||||||
|
params["healthCheckIdList"] = ",".join(health_check_id_list)
|
||||||
|
if search_health_check_name:
|
||||||
|
params["searchHealthCheckName"] = search_health_check_name
|
||||||
|
|
||||||
|
return self._get(url, params)
|
||||||
|
|
||||||
|
def get_health_check(self, health_check_id: str) -> dict:
|
||||||
|
"""
|
||||||
|
Health Check 상세 조회
|
||||||
|
|
||||||
|
Args:
|
||||||
|
health_check_id: Health Check ID
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: Health Check 상세 정보
|
||||||
|
"""
|
||||||
|
url = f"{self.base_url}/health-checks"
|
||||||
|
params = {"healthCheckIdList": health_check_id}
|
||||||
|
result = self._get(url, params)
|
||||||
|
health_checks = result.get("healthCheckList", [])
|
||||||
|
if health_checks:
|
||||||
|
return {"healthCheck": health_checks[0]}
|
||||||
|
raise NHNCloudAPIError(f"Health Check를 찾을 수 없습니다: {health_check_id}", code=404)
|
||||||
|
|
||||||
|
def create_health_check(
|
||||||
|
self,
|
||||||
|
health_check_name: str,
|
||||||
|
health_check_protocol: str,
|
||||||
|
health_check_port: int,
|
||||||
|
health_check_interval: int = 30,
|
||||||
|
health_check_timeout: int = 5,
|
||||||
|
health_check_retries: int = 2,
|
||||||
|
health_check_path: Optional[str] = None,
|
||||||
|
health_check_expected_codes: Optional[str] = None,
|
||||||
|
health_check_expected_body: Optional[str] = None,
|
||||||
|
health_check_request_header_list: Optional[List[dict]] = None,
|
||||||
|
) -> dict:
|
||||||
|
"""
|
||||||
|
Health Check 생성
|
||||||
|
|
||||||
|
Args:
|
||||||
|
health_check_name: Health Check 이름
|
||||||
|
health_check_protocol: 프로토콜 (HTTPS, HTTP, TCP)
|
||||||
|
health_check_port: 포트
|
||||||
|
health_check_interval: 체크 간격 (초)
|
||||||
|
health_check_timeout: 타임아웃 (초)
|
||||||
|
health_check_retries: 재시도 횟수
|
||||||
|
health_check_path: 경로 (HTTP/HTTPS 전용)
|
||||||
|
health_check_expected_codes: 예상 응답 코드 (HTTP/HTTPS 전용)
|
||||||
|
health_check_expected_body: 예상 응답 본문 (HTTP/HTTPS 전용)
|
||||||
|
health_check_request_header_list: 요청 헤더 목록 (HTTP/HTTPS 전용)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 생성된 Health Check 정보
|
||||||
|
"""
|
||||||
|
url = f"{self.base_url}/health-checks"
|
||||||
|
payload = {
|
||||||
|
"healthCheck": {
|
||||||
|
"healthCheckName": health_check_name,
|
||||||
|
"healthCheckProtocol": health_check_protocol,
|
||||||
|
"healthCheckPort": health_check_port,
|
||||||
|
"healthCheckInterval": health_check_interval,
|
||||||
|
"healthCheckTimeout": health_check_timeout,
|
||||||
|
"healthCheckRetries": health_check_retries,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# HTTP/HTTPS 전용 옵션
|
||||||
|
if health_check_path:
|
||||||
|
payload["healthCheck"]["healthCheckPath"] = health_check_path
|
||||||
|
if health_check_expected_codes:
|
||||||
|
payload["healthCheck"]["healthCheckExpectedCodes"] = health_check_expected_codes
|
||||||
|
if health_check_expected_body:
|
||||||
|
payload["healthCheck"]["healthCheckExpectedBody"] = health_check_expected_body
|
||||||
|
if health_check_request_header_list:
|
||||||
|
payload["healthCheck"]["healthCheckRequestHeaderList"] = health_check_request_header_list
|
||||||
|
|
||||||
|
logger.info(f"Health Check 생성 요청: {health_check_name}")
|
||||||
|
return self._post(url, payload)
|
||||||
|
|
||||||
|
def update_health_check(
|
||||||
|
self,
|
||||||
|
health_check_id: str,
|
||||||
|
health_check_name: Optional[str] = None,
|
||||||
|
health_check_protocol: Optional[str] = None,
|
||||||
|
health_check_port: Optional[int] = None,
|
||||||
|
health_check_interval: Optional[int] = None,
|
||||||
|
health_check_timeout: Optional[int] = None,
|
||||||
|
health_check_retries: Optional[int] = None,
|
||||||
|
health_check_path: Optional[str] = None,
|
||||||
|
health_check_expected_codes: Optional[str] = None,
|
||||||
|
health_check_expected_body: Optional[str] = None,
|
||||||
|
health_check_request_header_list: Optional[List[dict]] = None,
|
||||||
|
) -> dict:
|
||||||
|
"""
|
||||||
|
Health Check 수정
|
||||||
|
|
||||||
|
Args:
|
||||||
|
health_check_id: Health Check ID
|
||||||
|
(나머지 파라미터는 create_health_check과 동일)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 수정된 Health Check 정보
|
||||||
|
"""
|
||||||
|
url = f"{self.base_url}/health-checks/{health_check_id}"
|
||||||
|
health_check_data = {}
|
||||||
|
|
||||||
|
if health_check_name is not None:
|
||||||
|
health_check_data["healthCheckName"] = health_check_name
|
||||||
|
if health_check_protocol is not None:
|
||||||
|
health_check_data["healthCheckProtocol"] = health_check_protocol
|
||||||
|
if health_check_port is not None:
|
||||||
|
health_check_data["healthCheckPort"] = health_check_port
|
||||||
|
if health_check_interval is not None:
|
||||||
|
health_check_data["healthCheckInterval"] = health_check_interval
|
||||||
|
if health_check_timeout is not None:
|
||||||
|
health_check_data["healthCheckTimeout"] = health_check_timeout
|
||||||
|
if health_check_retries is not None:
|
||||||
|
health_check_data["healthCheckRetries"] = health_check_retries
|
||||||
|
if health_check_path is not None:
|
||||||
|
health_check_data["healthCheckPath"] = health_check_path
|
||||||
|
if health_check_expected_codes is not None:
|
||||||
|
health_check_data["healthCheckExpectedCodes"] = health_check_expected_codes
|
||||||
|
if health_check_expected_body is not None:
|
||||||
|
health_check_data["healthCheckExpectedBody"] = health_check_expected_body
|
||||||
|
if health_check_request_header_list is not None:
|
||||||
|
health_check_data["healthCheckRequestHeaderList"] = health_check_request_header_list
|
||||||
|
|
||||||
|
payload = {"healthCheck": health_check_data}
|
||||||
|
|
||||||
|
logger.info(f"Health Check 수정 요청: {health_check_id}")
|
||||||
|
return self._put(url, payload)
|
||||||
|
|
||||||
|
def delete_health_checks(self, health_check_id_list: List[str]) -> dict:
|
||||||
|
"""
|
||||||
|
Health Check 삭제
|
||||||
|
|
||||||
|
Args:
|
||||||
|
health_check_id_list: 삭제할 Health Check ID 목록
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 삭제 결과
|
||||||
|
"""
|
||||||
|
url = f"{self.base_url}/health-checks"
|
||||||
|
params = {"healthCheckIdList": ",".join(health_check_id_list)}
|
||||||
|
|
||||||
|
logger.info(f"Health Check 삭제 요청: {health_check_id_list}")
|
||||||
|
return self._delete(url, params)
|
||||||
@ -51,6 +51,13 @@ class ApiNks(BaseAPI):
|
|||||||
headers.update(extra_headers)
|
headers.update(extra_headers)
|
||||||
return headers
|
return headers
|
||||||
|
|
||||||
|
# ==================== Supports ====================
|
||||||
|
|
||||||
|
def get_supports(self) -> dict:
|
||||||
|
"""지원되는 Kubernetes 버전 및 작업 종류 조회"""
|
||||||
|
url = f"{self.nks_url}/v1/supports"
|
||||||
|
return self._get(url)
|
||||||
|
|
||||||
# ==================== Cluster ====================
|
# ==================== Cluster ====================
|
||||||
|
|
||||||
def get_cluster_list(self) -> dict:
|
def get_cluster_list(self) -> dict:
|
||||||
@ -195,6 +202,78 @@ class ApiNks(BaseAPI):
|
|||||||
logger.info(f"Private 클러스터 생성 요청: {cluster_name}")
|
logger.info(f"Private 클러스터 생성 요청: {cluster_name}")
|
||||||
return self._post(url, payload)
|
return self._post(url, payload)
|
||||||
|
|
||||||
|
def create_cluster(
|
||||||
|
self,
|
||||||
|
cluster_name: str,
|
||||||
|
vpc_id: str,
|
||||||
|
subnet_id: str,
|
||||||
|
instance_type: str,
|
||||||
|
keypair_name: str,
|
||||||
|
kubernetes_version: str,
|
||||||
|
availability_zone: str,
|
||||||
|
is_public: bool = True,
|
||||||
|
external_network_id: Optional[str] = None,
|
||||||
|
external_subnet_id: Optional[str] = None,
|
||||||
|
node_count: int = 1,
|
||||||
|
boot_volume_size: int = 50,
|
||||||
|
boot_volume_type: str = "General SSD",
|
||||||
|
node_image: Optional[str] = None,
|
||||||
|
) -> dict:
|
||||||
|
"""
|
||||||
|
클러스터 생성 (Public/Private 분기)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
cluster_name: 클러스터 이름
|
||||||
|
vpc_id: VPC ID
|
||||||
|
subnet_id: 서브넷 ID
|
||||||
|
instance_type: 인스턴스 타입 (Flavor ID)
|
||||||
|
keypair_name: Keypair 이름
|
||||||
|
kubernetes_version: Kubernetes 버전 (예: v1.28.3)
|
||||||
|
availability_zone: 가용 영역 (예: kr-pub-a)
|
||||||
|
is_public: Public 클러스터 여부 (기본 True)
|
||||||
|
external_network_id: 외부 네트워크 ID (Public 클러스터 필수)
|
||||||
|
external_subnet_id: 외부 서브넷 ID (Public 클러스터 필수)
|
||||||
|
node_count: 노드 수 (기본 1)
|
||||||
|
boot_volume_size: 부팅 볼륨 크기 (GB, 기본 50)
|
||||||
|
boot_volume_type: 볼륨 타입 (기본 "General SSD")
|
||||||
|
node_image: 노드 이미지 ID (기본 Ubuntu 20.04)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 생성된 클러스터 정보
|
||||||
|
"""
|
||||||
|
if is_public:
|
||||||
|
if not external_network_id or not external_subnet_id:
|
||||||
|
raise ValueError("Public 클러스터에는 external_network_id와 external_subnet_id가 필요합니다.")
|
||||||
|
return self.create_public_cluster(
|
||||||
|
cluster_name=cluster_name,
|
||||||
|
vpc_id=vpc_id,
|
||||||
|
subnet_id=subnet_id,
|
||||||
|
instance_type=instance_type,
|
||||||
|
keypair_name=keypair_name,
|
||||||
|
kubernetes_version=kubernetes_version,
|
||||||
|
external_network_id=external_network_id,
|
||||||
|
external_subnet_id=external_subnet_id,
|
||||||
|
availability_zone=availability_zone,
|
||||||
|
node_count=node_count,
|
||||||
|
boot_volume_size=boot_volume_size,
|
||||||
|
boot_volume_type=boot_volume_type,
|
||||||
|
node_image=node_image,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return self.create_private_cluster(
|
||||||
|
cluster_name=cluster_name,
|
||||||
|
vpc_id=vpc_id,
|
||||||
|
subnet_id=subnet_id,
|
||||||
|
instance_type=instance_type,
|
||||||
|
keypair_name=keypair_name,
|
||||||
|
kubernetes_version=kubernetes_version,
|
||||||
|
availability_zone=availability_zone,
|
||||||
|
node_count=node_count,
|
||||||
|
boot_volume_size=boot_volume_size,
|
||||||
|
boot_volume_type=boot_volume_type,
|
||||||
|
node_image=node_image,
|
||||||
|
)
|
||||||
|
|
||||||
def delete_cluster(self, cluster_name: str) -> dict:
|
def delete_cluster(self, cluster_name: str) -> dict:
|
||||||
"""클러스터 삭제"""
|
"""클러스터 삭제"""
|
||||||
url = f"{self.nks_url}/v1/clusters/{cluster_name}"
|
url = f"{self.nks_url}/v1/clusters/{cluster_name}"
|
||||||
@ -212,3 +291,349 @@ class ApiNks(BaseAPI):
|
|||||||
"""노드 그룹 상세 조회"""
|
"""노드 그룹 상세 조회"""
|
||||||
url = f"{self.nks_url}/v1/clusters/{cluster_name}/nodegroups/{nodegroup_name}"
|
url = f"{self.nks_url}/v1/clusters/{cluster_name}/nodegroups/{nodegroup_name}"
|
||||||
return self._get(url)
|
return self._get(url)
|
||||||
|
|
||||||
|
def create_nodegroup(
|
||||||
|
self,
|
||||||
|
cluster_name: str,
|
||||||
|
nodegroup_name: str,
|
||||||
|
instance_type: str,
|
||||||
|
node_count: int = 1,
|
||||||
|
availability_zone: Optional[str] = None,
|
||||||
|
boot_volume_size: int = 50,
|
||||||
|
boot_volume_type: str = "General SSD",
|
||||||
|
node_image: Optional[str] = None,
|
||||||
|
) -> dict:
|
||||||
|
"""
|
||||||
|
노드 그룹 생성
|
||||||
|
|
||||||
|
Args:
|
||||||
|
cluster_name: 클러스터 이름
|
||||||
|
nodegroup_name: 노드 그룹 이름
|
||||||
|
instance_type: 인스턴스 타입 (Flavor ID)
|
||||||
|
node_count: 노드 수 (기본 1)
|
||||||
|
availability_zone: 가용 영역 (예: kr-pub-a)
|
||||||
|
boot_volume_size: 부팅 볼륨 크기 (GB, 기본 50)
|
||||||
|
boot_volume_type: 볼륨 타입 (기본 "General SSD")
|
||||||
|
node_image: 노드 이미지 ID (기본 Ubuntu 20.04)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 생성된 노드 그룹 정보
|
||||||
|
"""
|
||||||
|
url = f"{self.nks_url}/v1/clusters/{cluster_name}/nodegroups"
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
"name": nodegroup_name,
|
||||||
|
"flavor_id": instance_type,
|
||||||
|
"node_count": node_count,
|
||||||
|
"labels": {
|
||||||
|
"boot_volume_size": str(boot_volume_size),
|
||||||
|
"boot_volume_type": boot_volume_type,
|
||||||
|
"node_image": node_image or self.default_node_image,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if availability_zone:
|
||||||
|
payload["labels"]["availability_zone"] = availability_zone
|
||||||
|
|
||||||
|
logger.info(f"노드 그룹 생성 요청: {cluster_name}/{nodegroup_name}")
|
||||||
|
return self._post(url, payload)
|
||||||
|
|
||||||
|
def delete_nodegroup(self, cluster_name: str, nodegroup_name: str) -> dict:
|
||||||
|
"""노드 그룹 삭제"""
|
||||||
|
url = f"{self.nks_url}/v1/clusters/{cluster_name}/nodegroups/{nodegroup_name}"
|
||||||
|
logger.info(f"노드 그룹 삭제 요청: {cluster_name}/{nodegroup_name}")
|
||||||
|
return self._delete(url)
|
||||||
|
|
||||||
|
def start_node(self, cluster_name: str, nodegroup_name: str, node_id: str) -> dict:
|
||||||
|
"""
|
||||||
|
워커 노드 시작
|
||||||
|
|
||||||
|
Args:
|
||||||
|
cluster_name: 클러스터 이름
|
||||||
|
nodegroup_name: 노드 그룹 이름
|
||||||
|
node_id: 노드 ID
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 응답 결과
|
||||||
|
"""
|
||||||
|
url = f"{self.nks_url}/v1/clusters/{cluster_name}/nodegroups/{nodegroup_name}/start_node"
|
||||||
|
payload = {"node_id": node_id}
|
||||||
|
logger.info(f"워커 노드 시작 요청: {cluster_name}/{nodegroup_name}/{node_id}")
|
||||||
|
return self._post(url, payload)
|
||||||
|
|
||||||
|
def stop_node(self, cluster_name: str, nodegroup_name: str, node_id: str) -> dict:
|
||||||
|
"""
|
||||||
|
워커 노드 중지
|
||||||
|
|
||||||
|
Args:
|
||||||
|
cluster_name: 클러스터 이름
|
||||||
|
nodegroup_name: 노드 그룹 이름
|
||||||
|
node_id: 노드 ID
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 응답 결과
|
||||||
|
"""
|
||||||
|
url = f"{self.nks_url}/v1/clusters/{cluster_name}/nodegroups/{nodegroup_name}/stop_node"
|
||||||
|
payload = {"node_id": node_id}
|
||||||
|
logger.info(f"워커 노드 중지 요청: {cluster_name}/{nodegroup_name}/{node_id}")
|
||||||
|
return self._post(url, payload)
|
||||||
|
|
||||||
|
# ==================== Cluster Operations ====================
|
||||||
|
|
||||||
|
def resize_cluster(self, cluster_name: str, node_count: int) -> dict:
|
||||||
|
"""
|
||||||
|
클러스터 노드 수 조정 (리사이즈)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
cluster_name: 클러스터 이름
|
||||||
|
node_count: 조정할 노드 수
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 응답 결과
|
||||||
|
"""
|
||||||
|
url = f"{self.nks_url}/v1/clusters/{cluster_name}/actions/resize"
|
||||||
|
payload = {"node_count": node_count}
|
||||||
|
logger.info(f"클러스터 리사이즈 요청: {cluster_name}, node_count={node_count}")
|
||||||
|
return self._post(url, payload)
|
||||||
|
|
||||||
|
def upgrade_cluster(self, cluster_name: str, kubernetes_version: str) -> dict:
|
||||||
|
"""
|
||||||
|
클러스터 Kubernetes 버전 업그레이드
|
||||||
|
|
||||||
|
Args:
|
||||||
|
cluster_name: 클러스터 이름
|
||||||
|
kubernetes_version: 업그레이드할 Kubernetes 버전
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 응답 결과
|
||||||
|
"""
|
||||||
|
url = f"{self.nks_url}/v1/clusters/{cluster_name}/actions/upgrade"
|
||||||
|
payload = {"kube_tag": kubernetes_version}
|
||||||
|
logger.info(f"클러스터 업그레이드 요청: {cluster_name}, version={kubernetes_version}")
|
||||||
|
return self._post(url, payload)
|
||||||
|
|
||||||
|
def get_cluster_events(self, cluster_uuid: str) -> dict:
|
||||||
|
"""
|
||||||
|
클러스터 작업 이력 목록 조회
|
||||||
|
|
||||||
|
Args:
|
||||||
|
cluster_uuid: 클러스터 UUID
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 작업 이력 목록
|
||||||
|
"""
|
||||||
|
url = f"{self.nks_url}/v1/clusters/{cluster_uuid}/events"
|
||||||
|
return self._get(url)
|
||||||
|
|
||||||
|
def get_cluster_event(self, cluster_uuid: str, event_uuid: str) -> dict:
|
||||||
|
"""
|
||||||
|
클러스터 작업 이력 상세 조회
|
||||||
|
|
||||||
|
Args:
|
||||||
|
cluster_uuid: 클러스터 UUID
|
||||||
|
event_uuid: 작업 이력 UUID
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 작업 이력 상세 정보
|
||||||
|
"""
|
||||||
|
url = f"{self.nks_url}/v1/clusters/{cluster_uuid}/events/{event_uuid}"
|
||||||
|
return self._get(url)
|
||||||
|
|
||||||
|
# ==================== Autoscaler ====================
|
||||||
|
|
||||||
|
def get_autoscale_config(self, cluster_name: str, nodegroup_name: str) -> dict:
|
||||||
|
"""
|
||||||
|
오토스케일러 설정 조회
|
||||||
|
|
||||||
|
Args:
|
||||||
|
cluster_name: 클러스터 이름
|
||||||
|
nodegroup_name: 노드 그룹 이름
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 오토스케일러 설정
|
||||||
|
"""
|
||||||
|
url = f"{self.nks_url}/v1/clusters/{cluster_name}/nodegroups/{nodegroup_name}/autoscale"
|
||||||
|
return self._get(url)
|
||||||
|
|
||||||
|
def set_autoscale_config(
|
||||||
|
self,
|
||||||
|
cluster_name: str,
|
||||||
|
nodegroup_name: str,
|
||||||
|
ca_enable: bool,
|
||||||
|
ca_max_node_count: Optional[int] = None,
|
||||||
|
ca_min_node_count: Optional[int] = None,
|
||||||
|
ca_scale_down_enable: Optional[bool] = None,
|
||||||
|
ca_scale_down_delay_after_add: Optional[int] = None,
|
||||||
|
ca_scale_down_unneeded_time: Optional[int] = None,
|
||||||
|
) -> dict:
|
||||||
|
"""
|
||||||
|
오토스케일러 설정 변경
|
||||||
|
|
||||||
|
Args:
|
||||||
|
cluster_name: 클러스터 이름
|
||||||
|
nodegroup_name: 노드 그룹 이름
|
||||||
|
ca_enable: 오토스케일러 활성화 여부
|
||||||
|
ca_max_node_count: 최대 노드 수
|
||||||
|
ca_min_node_count: 최소 노드 수
|
||||||
|
ca_scale_down_enable: 스케일 다운 활성화 여부
|
||||||
|
ca_scale_down_delay_after_add: 스케일 다운 지연 시간 (분)
|
||||||
|
ca_scale_down_unneeded_time: 불필요 노드 대기 시간 (분)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 변경된 오토스케일러 설정
|
||||||
|
"""
|
||||||
|
url = f"{self.nks_url}/v1/clusters/{cluster_name}/nodegroups/{nodegroup_name}/autoscale"
|
||||||
|
|
||||||
|
payload = {"ca_enable": ca_enable}
|
||||||
|
|
||||||
|
if ca_max_node_count is not None:
|
||||||
|
payload["ca_max_node_count"] = ca_max_node_count
|
||||||
|
if ca_min_node_count is not None:
|
||||||
|
payload["ca_min_node_count"] = ca_min_node_count
|
||||||
|
if ca_scale_down_enable is not None:
|
||||||
|
payload["ca_scale_down_enable"] = ca_scale_down_enable
|
||||||
|
if ca_scale_down_delay_after_add is not None:
|
||||||
|
payload["ca_scale_down_delay_after_add"] = ca_scale_down_delay_after_add
|
||||||
|
if ca_scale_down_unneeded_time is not None:
|
||||||
|
payload["ca_scale_down_unneeded_time"] = ca_scale_down_unneeded_time
|
||||||
|
|
||||||
|
logger.info(f"오토스케일러 설정 변경 요청: {cluster_name}/{nodegroup_name}")
|
||||||
|
return self._post(url, payload)
|
||||||
|
|
||||||
|
# ==================== Node Group Configuration ====================
|
||||||
|
|
||||||
|
def upgrade_nodegroup(
|
||||||
|
self,
|
||||||
|
cluster_name: str,
|
||||||
|
nodegroup_name: str,
|
||||||
|
kubernetes_version: str,
|
||||||
|
max_unavailable_worker: int = 1,
|
||||||
|
) -> dict:
|
||||||
|
"""
|
||||||
|
노드 그룹 Kubernetes 버전 업그레이드
|
||||||
|
|
||||||
|
Args:
|
||||||
|
cluster_name: 클러스터 이름
|
||||||
|
nodegroup_name: 노드 그룹 이름
|
||||||
|
kubernetes_version: 업그레이드할 Kubernetes 버전
|
||||||
|
max_unavailable_worker: 동시 업그레이드 가능한 노드 수
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 응답 결과
|
||||||
|
"""
|
||||||
|
url = f"{self.nks_url}/v1/clusters/{cluster_name}/nodegroups/{nodegroup_name}/upgrade"
|
||||||
|
payload = {
|
||||||
|
"kube_tag": kubernetes_version,
|
||||||
|
"max_unavailable_worker": max_unavailable_worker,
|
||||||
|
}
|
||||||
|
logger.info(f"노드 그룹 업그레이드 요청: {cluster_name}/{nodegroup_name}, version={kubernetes_version}")
|
||||||
|
return self._post(url, payload)
|
||||||
|
|
||||||
|
def set_nodegroup_userscript(
|
||||||
|
self,
|
||||||
|
cluster_name: str,
|
||||||
|
nodegroup_name: str,
|
||||||
|
userscript: str,
|
||||||
|
) -> dict:
|
||||||
|
"""
|
||||||
|
노드 그룹 사용자 스크립트 설정
|
||||||
|
|
||||||
|
Args:
|
||||||
|
cluster_name: 클러스터 이름
|
||||||
|
nodegroup_name: 노드 그룹 이름
|
||||||
|
userscript: 사용자 스크립트 내용
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 응답 결과
|
||||||
|
"""
|
||||||
|
url = f"{self.nks_url}/v1/clusters/{cluster_name}/nodegroups/{nodegroup_name}/userscript"
|
||||||
|
payload = {"userscript": userscript}
|
||||||
|
logger.info(f"노드 그룹 사용자 스크립트 설정 요청: {cluster_name}/{nodegroup_name}")
|
||||||
|
return self._post(url, payload)
|
||||||
|
|
||||||
|
def update_nodegroup(
|
||||||
|
self,
|
||||||
|
cluster_name: str,
|
||||||
|
nodegroup_name: str,
|
||||||
|
instance_type: Optional[str] = None,
|
||||||
|
node_count: Optional[int] = None,
|
||||||
|
) -> dict:
|
||||||
|
"""
|
||||||
|
노드 그룹 설정 변경 (인스턴스 타입, 노드 수 등)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
cluster_name: 클러스터 이름
|
||||||
|
nodegroup_name: 노드 그룹 이름
|
||||||
|
instance_type: 인스턴스 타입 (Flavor ID)
|
||||||
|
node_count: 노드 수
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 변경된 노드 그룹 정보
|
||||||
|
"""
|
||||||
|
url = f"{self.nks_url}/v1/clusters/{cluster_name}/nodegroups/{nodegroup_name}"
|
||||||
|
|
||||||
|
payload = {}
|
||||||
|
if instance_type is not None:
|
||||||
|
payload["flavor_id"] = instance_type
|
||||||
|
if node_count is not None:
|
||||||
|
payload["node_count"] = node_count
|
||||||
|
|
||||||
|
logger.info(f"노드 그룹 설정 변경 요청: {cluster_name}/{nodegroup_name}")
|
||||||
|
return self._patch(url, payload)
|
||||||
|
|
||||||
|
# ==================== Certificates ====================
|
||||||
|
|
||||||
|
def renew_certificates(self, cluster_name: str) -> dict:
|
||||||
|
"""
|
||||||
|
클러스터 인증서 갱신
|
||||||
|
|
||||||
|
Args:
|
||||||
|
cluster_name: 클러스터 이름
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 응답 결과
|
||||||
|
"""
|
||||||
|
url = f"{self.nks_url}/v1/certificates/{cluster_name}"
|
||||||
|
logger.info(f"클러스터 인증서 갱신 요청: {cluster_name}")
|
||||||
|
return self._patch(url, {})
|
||||||
|
|
||||||
|
# ==================== API Endpoint IP ACL ====================
|
||||||
|
|
||||||
|
def get_api_endpoint_ipacl(self, cluster_name: str) -> dict:
|
||||||
|
"""
|
||||||
|
API 엔드포인트 IP 접근 제어 조회
|
||||||
|
|
||||||
|
Args:
|
||||||
|
cluster_name: 클러스터 이름
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: IP 접근 제어 설정
|
||||||
|
"""
|
||||||
|
url = f"{self.nks_url}/v1/clusters/{cluster_name}/api_ep_ipacl"
|
||||||
|
return self._get(url)
|
||||||
|
|
||||||
|
def set_api_endpoint_ipacl(
|
||||||
|
self,
|
||||||
|
cluster_name: str,
|
||||||
|
enable: bool,
|
||||||
|
allowed_cidrs: Optional[list] = None,
|
||||||
|
) -> dict:
|
||||||
|
"""
|
||||||
|
API 엔드포인트 IP 접근 제어 설정
|
||||||
|
|
||||||
|
Args:
|
||||||
|
cluster_name: 클러스터 이름
|
||||||
|
enable: IP 접근 제어 활성화 여부
|
||||||
|
allowed_cidrs: 허용할 CIDR 목록 (예: ["192.168.0.0/24", "10.0.0.0/8"])
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 변경된 IP 접근 제어 설정
|
||||||
|
"""
|
||||||
|
url = f"{self.nks_url}/v1/clusters/{cluster_name}/api_ep_ipacl"
|
||||||
|
|
||||||
|
payload = {"enable": enable}
|
||||||
|
if allowed_cidrs is not None:
|
||||||
|
payload["allowed_cidrs"] = allowed_cidrs
|
||||||
|
|
||||||
|
logger.info(f"API 엔드포인트 IP 접근 제어 설정 요청: {cluster_name}")
|
||||||
|
return self._post(url, payload)
|
||||||
|
|||||||
@ -148,17 +148,9 @@ class NksClusterSerializer(serializers.Serializer):
|
|||||||
help_text="가용 영역 (예: kr-pub-a)",
|
help_text="가용 영역 (예: kr-pub-a)",
|
||||||
)
|
)
|
||||||
is_public = serializers.BooleanField(
|
is_public = serializers.BooleanField(
|
||||||
help_text="Public 클러스터 여부 (외부 접근 가능)",
|
help_text="Public 클러스터 여부 (외부 접근 가능, True 선택 시 External Network 자동 설정)",
|
||||||
default=True,
|
default=True,
|
||||||
)
|
)
|
||||||
external_network_id = serializers.CharField(
|
|
||||||
help_text="외부 네트워크 ID (Public 클러스터 필수)",
|
|
||||||
required=False,
|
|
||||||
)
|
|
||||||
external_subnet_id = serializers.CharField(
|
|
||||||
help_text="외부 서브넷 ID (Public 클러스터 필수)",
|
|
||||||
required=False,
|
|
||||||
)
|
|
||||||
node_count = serializers.IntegerField(
|
node_count = serializers.IntegerField(
|
||||||
help_text="노드 수",
|
help_text="노드 수",
|
||||||
default=1,
|
default=1,
|
||||||
@ -176,19 +168,6 @@ class NksClusterSerializer(serializers.Serializer):
|
|||||||
default="General SSD",
|
default="General SSD",
|
||||||
)
|
)
|
||||||
|
|
||||||
def validate(self, data):
|
|
||||||
"""Public 클러스터인 경우 external 관련 필드 필수"""
|
|
||||||
if data.get("is_public", True):
|
|
||||||
if not data.get("external_network_id"):
|
|
||||||
raise serializers.ValidationError(
|
|
||||||
{"external_network_id": "Public 클러스터에는 외부 네트워크 ID가 필요합니다."}
|
|
||||||
)
|
|
||||||
if not data.get("external_subnet_id"):
|
|
||||||
raise serializers.ValidationError(
|
|
||||||
{"external_subnet_id": "Public 클러스터에는 외부 서브넷 ID가 필요합니다."}
|
|
||||||
)
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
# ==================== Storage ====================
|
# ==================== Storage ====================
|
||||||
|
|
||||||
@ -200,3 +179,468 @@ class StorageContainerSerializer(serializers.Serializer):
|
|||||||
help_text="컨테이너 이름",
|
help_text="컨테이너 이름",
|
||||||
max_length=255,
|
max_length=255,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# ==================== NKS Advanced ====================
|
||||||
|
|
||||||
|
|
||||||
|
class NksNodeGroupSerializer(serializers.Serializer):
|
||||||
|
"""NKS 노드 그룹 생성 요청"""
|
||||||
|
|
||||||
|
nodegroup_name = serializers.CharField(
|
||||||
|
help_text="노드 그룹 이름",
|
||||||
|
max_length=255,
|
||||||
|
)
|
||||||
|
instance_type = serializers.CharField(
|
||||||
|
help_text="인스턴스 타입 (Flavor ID)",
|
||||||
|
)
|
||||||
|
node_count = serializers.IntegerField(
|
||||||
|
help_text="노드 수",
|
||||||
|
default=1,
|
||||||
|
min_value=1,
|
||||||
|
max_value=100,
|
||||||
|
)
|
||||||
|
availability_zone = serializers.CharField(
|
||||||
|
help_text="가용 영역 (예: kr-pub-a)",
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
boot_volume_size = serializers.IntegerField(
|
||||||
|
help_text="부팅 볼륨 크기 (GB)",
|
||||||
|
default=50,
|
||||||
|
min_value=50,
|
||||||
|
max_value=1000,
|
||||||
|
)
|
||||||
|
boot_volume_type = serializers.CharField(
|
||||||
|
help_text="볼륨 타입",
|
||||||
|
default="General SSD",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class NksNodeActionSerializer(serializers.Serializer):
|
||||||
|
"""워커 노드 액션 요청"""
|
||||||
|
|
||||||
|
node_id = serializers.CharField(
|
||||||
|
help_text="노드 ID",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class NksClusterResizeSerializer(serializers.Serializer):
|
||||||
|
"""클러스터 리사이즈 요청"""
|
||||||
|
|
||||||
|
node_count = serializers.IntegerField(
|
||||||
|
help_text="조정할 노드 수",
|
||||||
|
min_value=1,
|
||||||
|
max_value=100,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class NksClusterUpgradeSerializer(serializers.Serializer):
|
||||||
|
"""클러스터 업그레이드 요청"""
|
||||||
|
|
||||||
|
kubernetes_version = serializers.CharField(
|
||||||
|
help_text="업그레이드할 Kubernetes 버전 (예: v1.28.3)",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class NksAutoscaleConfigSerializer(serializers.Serializer):
|
||||||
|
"""오토스케일러 설정 요청"""
|
||||||
|
|
||||||
|
ca_enable = serializers.BooleanField(
|
||||||
|
help_text="오토스케일러 활성화 여부",
|
||||||
|
)
|
||||||
|
ca_max_node_count = serializers.IntegerField(
|
||||||
|
help_text="최대 노드 수",
|
||||||
|
required=False,
|
||||||
|
min_value=1,
|
||||||
|
max_value=100,
|
||||||
|
)
|
||||||
|
ca_min_node_count = serializers.IntegerField(
|
||||||
|
help_text="최소 노드 수",
|
||||||
|
required=False,
|
||||||
|
min_value=0,
|
||||||
|
max_value=100,
|
||||||
|
)
|
||||||
|
ca_scale_down_enable = serializers.BooleanField(
|
||||||
|
help_text="스케일 다운 활성화 여부",
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
ca_scale_down_delay_after_add = serializers.IntegerField(
|
||||||
|
help_text="스케일 다운 지연 시간 (분)",
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
ca_scale_down_unneeded_time = serializers.IntegerField(
|
||||||
|
help_text="불필요 노드 대기 시간 (분)",
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class NksNodeGroupUpgradeSerializer(serializers.Serializer):
|
||||||
|
"""노드 그룹 업그레이드 요청"""
|
||||||
|
|
||||||
|
kubernetes_version = serializers.CharField(
|
||||||
|
help_text="업그레이드할 Kubernetes 버전",
|
||||||
|
)
|
||||||
|
max_unavailable_worker = serializers.IntegerField(
|
||||||
|
help_text="동시 업그레이드 가능한 노드 수",
|
||||||
|
default=1,
|
||||||
|
min_value=1,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class NksNodeGroupUpdateSerializer(serializers.Serializer):
|
||||||
|
"""노드 그룹 설정 변경 요청"""
|
||||||
|
|
||||||
|
instance_type = serializers.CharField(
|
||||||
|
help_text="인스턴스 타입 (Flavor ID)",
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
node_count = serializers.IntegerField(
|
||||||
|
help_text="노드 수",
|
||||||
|
required=False,
|
||||||
|
min_value=0,
|
||||||
|
max_value=100,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class NksUserScriptSerializer(serializers.Serializer):
|
||||||
|
"""사용자 스크립트 설정 요청"""
|
||||||
|
|
||||||
|
userscript = serializers.CharField(
|
||||||
|
help_text="사용자 스크립트 내용",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class NksApiEndpointIpAclSerializer(serializers.Serializer):
|
||||||
|
"""API 엔드포인트 IP 접근 제어 요청"""
|
||||||
|
|
||||||
|
enable = serializers.BooleanField(
|
||||||
|
help_text="IP 접근 제어 활성화 여부",
|
||||||
|
)
|
||||||
|
allowed_cidrs = serializers.ListField(
|
||||||
|
child=serializers.CharField(),
|
||||||
|
help_text="허용할 CIDR 목록",
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# ==================== DNS Plus ====================
|
||||||
|
|
||||||
|
|
||||||
|
class DnsZoneSerializer(serializers.Serializer):
|
||||||
|
"""DNS Zone 생성 요청"""
|
||||||
|
|
||||||
|
zone_name = serializers.CharField(
|
||||||
|
help_text="Zone 이름 (도메인, 예: example.com.)",
|
||||||
|
max_length=255,
|
||||||
|
)
|
||||||
|
description = serializers.CharField(
|
||||||
|
help_text="설명",
|
||||||
|
required=False,
|
||||||
|
allow_blank=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DnsZoneUpdateSerializer(serializers.Serializer):
|
||||||
|
"""DNS Zone 수정 요청"""
|
||||||
|
|
||||||
|
description = serializers.CharField(
|
||||||
|
help_text="설명",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DnsRecordSerializer(serializers.Serializer):
|
||||||
|
"""DNS 레코드"""
|
||||||
|
|
||||||
|
recordContent = serializers.CharField(
|
||||||
|
help_text="레코드 내용",
|
||||||
|
)
|
||||||
|
recordDisabled = serializers.BooleanField(
|
||||||
|
help_text="비활성화 여부",
|
||||||
|
default=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DnsRecordSetSerializer(serializers.Serializer):
|
||||||
|
"""DNS 레코드 세트 생성 요청"""
|
||||||
|
|
||||||
|
recordset_name = serializers.CharField(
|
||||||
|
help_text="레코드 세트 이름",
|
||||||
|
max_length=255,
|
||||||
|
)
|
||||||
|
recordset_type = serializers.ChoiceField(
|
||||||
|
choices=["A", "AAAA", "CAA", "CNAME", "MX", "NAPTR", "PTR", "TXT", "SRV", "NS"],
|
||||||
|
help_text="레코드 타입",
|
||||||
|
)
|
||||||
|
recordset_ttl = serializers.IntegerField(
|
||||||
|
help_text="TTL (초)",
|
||||||
|
min_value=1,
|
||||||
|
max_value=2147483647,
|
||||||
|
)
|
||||||
|
record_list = serializers.ListField(
|
||||||
|
child=DnsRecordSerializer(),
|
||||||
|
help_text="레코드 목록",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DnsRecordSetUpdateSerializer(serializers.Serializer):
|
||||||
|
"""DNS 레코드 세트 수정 요청"""
|
||||||
|
|
||||||
|
recordset_type = serializers.ChoiceField(
|
||||||
|
choices=["A", "AAAA", "CAA", "CNAME", "MX", "NAPTR", "PTR", "TXT", "SRV", "NS"],
|
||||||
|
help_text="레코드 타입",
|
||||||
|
)
|
||||||
|
recordset_ttl = serializers.IntegerField(
|
||||||
|
help_text="TTL (초)",
|
||||||
|
min_value=1,
|
||||||
|
max_value=2147483647,
|
||||||
|
)
|
||||||
|
record_list = serializers.ListField(
|
||||||
|
child=DnsRecordSerializer(),
|
||||||
|
help_text="레코드 목록",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DnsEndpointSerializer(serializers.Serializer):
|
||||||
|
"""Pool 엔드포인트"""
|
||||||
|
|
||||||
|
endpointAddress = serializers.CharField(
|
||||||
|
help_text="엔드포인트 주소 (IP 또는 도메인)",
|
||||||
|
)
|
||||||
|
endpointWeight = serializers.FloatField(
|
||||||
|
help_text="가중치 (0~1.00)",
|
||||||
|
min_value=0,
|
||||||
|
max_value=1.0,
|
||||||
|
default=1.0,
|
||||||
|
)
|
||||||
|
endpointDisabled = serializers.BooleanField(
|
||||||
|
help_text="비활성화 여부",
|
||||||
|
default=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DnsPoolSerializer(serializers.Serializer):
|
||||||
|
"""Pool 생성 요청"""
|
||||||
|
|
||||||
|
pool_name = serializers.CharField(
|
||||||
|
help_text="Pool 이름",
|
||||||
|
max_length=255,
|
||||||
|
)
|
||||||
|
endpoint_list = serializers.ListField(
|
||||||
|
child=DnsEndpointSerializer(),
|
||||||
|
help_text="엔드포인트 목록",
|
||||||
|
)
|
||||||
|
pool_disabled = serializers.BooleanField(
|
||||||
|
help_text="비활성화 여부",
|
||||||
|
default=False,
|
||||||
|
)
|
||||||
|
health_check_id = serializers.CharField(
|
||||||
|
help_text="Health Check ID",
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DnsPoolUpdateSerializer(serializers.Serializer):
|
||||||
|
"""Pool 수정 요청"""
|
||||||
|
|
||||||
|
pool_name = serializers.CharField(
|
||||||
|
help_text="Pool 이름",
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
endpoint_list = serializers.ListField(
|
||||||
|
child=DnsEndpointSerializer(),
|
||||||
|
help_text="엔드포인트 목록",
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
pool_disabled = serializers.BooleanField(
|
||||||
|
help_text="비활성화 여부",
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
health_check_id = serializers.CharField(
|
||||||
|
help_text="Health Check ID",
|
||||||
|
required=False,
|
||||||
|
allow_null=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DnsConnectedPoolSerializer(serializers.Serializer):
|
||||||
|
"""GSLB 연결 Pool"""
|
||||||
|
|
||||||
|
poolId = serializers.CharField(
|
||||||
|
help_text="Pool ID",
|
||||||
|
)
|
||||||
|
connectedPoolOrder = serializers.IntegerField(
|
||||||
|
help_text="연결 순서 (우선순위)",
|
||||||
|
min_value=1,
|
||||||
|
)
|
||||||
|
connectedPoolRegionContent = serializers.CharField(
|
||||||
|
help_text="지역 콘텐츠 (GEOLOCATION 규칙 사용 시)",
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DnsGslbSerializer(serializers.Serializer):
|
||||||
|
"""GSLB 생성 요청"""
|
||||||
|
|
||||||
|
gslb_name = serializers.CharField(
|
||||||
|
help_text="GSLB 이름",
|
||||||
|
max_length=255,
|
||||||
|
)
|
||||||
|
gslb_ttl = serializers.IntegerField(
|
||||||
|
help_text="TTL (초)",
|
||||||
|
min_value=1,
|
||||||
|
max_value=2147483647,
|
||||||
|
)
|
||||||
|
gslb_routing_rule = serializers.ChoiceField(
|
||||||
|
choices=["FAILOVER", "RANDOM", "GEOLOCATION"],
|
||||||
|
help_text="라우팅 규칙",
|
||||||
|
)
|
||||||
|
gslb_disabled = serializers.BooleanField(
|
||||||
|
help_text="비활성화 여부",
|
||||||
|
default=False,
|
||||||
|
)
|
||||||
|
connected_pool_list = serializers.ListField(
|
||||||
|
child=DnsConnectedPoolSerializer(),
|
||||||
|
help_text="연결된 Pool 목록",
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DnsGslbUpdateSerializer(serializers.Serializer):
|
||||||
|
"""GSLB 수정 요청"""
|
||||||
|
|
||||||
|
gslb_name = serializers.CharField(
|
||||||
|
help_text="GSLB 이름",
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
gslb_ttl = serializers.IntegerField(
|
||||||
|
help_text="TTL (초)",
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
gslb_routing_rule = serializers.ChoiceField(
|
||||||
|
choices=["FAILOVER", "RANDOM", "GEOLOCATION"],
|
||||||
|
help_text="라우팅 규칙",
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
gslb_disabled = serializers.BooleanField(
|
||||||
|
help_text="비활성화 여부",
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
connected_pool_list = serializers.ListField(
|
||||||
|
child=DnsConnectedPoolSerializer(),
|
||||||
|
help_text="연결된 Pool 목록",
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DnsPoolConnectSerializer(serializers.Serializer):
|
||||||
|
"""GSLB에 Pool 연결 요청"""
|
||||||
|
|
||||||
|
connected_pool_order = serializers.IntegerField(
|
||||||
|
help_text="연결 순서 (우선순위)",
|
||||||
|
min_value=1,
|
||||||
|
)
|
||||||
|
connected_pool_region_content = serializers.CharField(
|
||||||
|
help_text="지역 콘텐츠 (GEOLOCATION 규칙 사용 시)",
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DnsHealthCheckSerializer(serializers.Serializer):
|
||||||
|
"""Health Check 생성 요청"""
|
||||||
|
|
||||||
|
health_check_name = serializers.CharField(
|
||||||
|
help_text="Health Check 이름",
|
||||||
|
max_length=255,
|
||||||
|
)
|
||||||
|
health_check_protocol = serializers.ChoiceField(
|
||||||
|
choices=["HTTPS", "HTTP", "TCP"],
|
||||||
|
help_text="프로토콜",
|
||||||
|
)
|
||||||
|
health_check_port = serializers.IntegerField(
|
||||||
|
help_text="포트",
|
||||||
|
min_value=1,
|
||||||
|
max_value=65535,
|
||||||
|
)
|
||||||
|
health_check_interval = serializers.IntegerField(
|
||||||
|
help_text="체크 간격 (초)",
|
||||||
|
default=30,
|
||||||
|
min_value=10,
|
||||||
|
max_value=3600,
|
||||||
|
)
|
||||||
|
health_check_timeout = serializers.IntegerField(
|
||||||
|
help_text="타임아웃 (초)",
|
||||||
|
default=5,
|
||||||
|
min_value=1,
|
||||||
|
max_value=10,
|
||||||
|
)
|
||||||
|
health_check_retries = serializers.IntegerField(
|
||||||
|
help_text="재시도 횟수",
|
||||||
|
default=2,
|
||||||
|
min_value=1,
|
||||||
|
max_value=10,
|
||||||
|
)
|
||||||
|
# HTTP/HTTPS 전용
|
||||||
|
health_check_path = serializers.CharField(
|
||||||
|
help_text="경로 (HTTP/HTTPS 전용)",
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
health_check_expected_codes = serializers.CharField(
|
||||||
|
help_text="예상 응답 코드 (HTTP/HTTPS 전용)",
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
health_check_expected_body = serializers.CharField(
|
||||||
|
help_text="예상 응답 본문 (HTTP/HTTPS 전용)",
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DnsHealthCheckUpdateSerializer(serializers.Serializer):
|
||||||
|
"""Health Check 수정 요청"""
|
||||||
|
|
||||||
|
health_check_name = serializers.CharField(
|
||||||
|
help_text="Health Check 이름",
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
health_check_protocol = serializers.ChoiceField(
|
||||||
|
choices=["HTTPS", "HTTP", "TCP"],
|
||||||
|
help_text="프로토콜",
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
health_check_port = serializers.IntegerField(
|
||||||
|
help_text="포트",
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
health_check_interval = serializers.IntegerField(
|
||||||
|
help_text="체크 간격 (초)",
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
health_check_timeout = serializers.IntegerField(
|
||||||
|
help_text="타임아웃 (초)",
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
health_check_retries = serializers.IntegerField(
|
||||||
|
help_text="재시도 횟수",
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
health_check_path = serializers.CharField(
|
||||||
|
help_text="경로 (HTTP/HTTPS 전용)",
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
health_check_expected_codes = serializers.CharField(
|
||||||
|
help_text="예상 응답 코드 (HTTP/HTTPS 전용)",
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
health_check_expected_body = serializers.CharField(
|
||||||
|
help_text="예상 응답 본문 (HTTP/HTTPS 전용)",
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DnsIdListSerializer(serializers.Serializer):
|
||||||
|
"""ID 목록 요청"""
|
||||||
|
|
||||||
|
id_list = serializers.ListField(
|
||||||
|
child=serializers.CharField(),
|
||||||
|
help_text="ID 목록",
|
||||||
|
min_length=1,
|
||||||
|
)
|
||||||
|
|||||||
@ -141,13 +141,12 @@ def create_instance_async(region, tenant_id, token, instance_data):
|
|||||||
return task
|
return task
|
||||||
|
|
||||||
|
|
||||||
def create_nks_cluster_async(region, tenant_id, token, cluster_data):
|
def create_nks_cluster_async(region, token, cluster_data):
|
||||||
"""
|
"""
|
||||||
NKS 클러스터 비동기 생성
|
NKS 클러스터 비동기 생성
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
region: 리전
|
region: 리전
|
||||||
tenant_id: 테넌트 ID
|
|
||||||
token: API 토큰
|
token: API 토큰
|
||||||
cluster_data: 클러스터 생성 데이터 (dict)
|
cluster_data: 클러스터 생성 데이터 (dict)
|
||||||
|
|
||||||
@ -165,7 +164,7 @@ def create_nks_cluster_async(region, tenant_id, token, cluster_data):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# API 객체 생성
|
# API 객체 생성
|
||||||
api = ApiNks(region, tenant_id, token)
|
api = ApiNks(region, token)
|
||||||
|
|
||||||
# 비동기 실행
|
# 비동기 실행
|
||||||
execute_async_task(
|
execute_async_task(
|
||||||
|
|||||||
47
nhn/urls.py
47
nhn/urls.py
@ -39,14 +39,59 @@ urlpatterns = [
|
|||||||
path("tasks/", views.AsyncTaskListView.as_view(), name="task-list"),
|
path("tasks/", views.AsyncTaskListView.as_view(), name="task-list"),
|
||||||
path("tasks/<str:task_id>/", views.AsyncTaskDetailView.as_view(), name="task-detail"),
|
path("tasks/<str:task_id>/", views.AsyncTaskDetailView.as_view(), name="task-detail"),
|
||||||
|
|
||||||
# ==================== NKS ====================
|
# ==================== NKS Cluster ====================
|
||||||
|
path("nks/supports/", views.NksSupportsView.as_view(), name="nks-supports"),
|
||||||
path("nks/clusters/", views.NksClusterListView.as_view(), name="nks-cluster-list"),
|
path("nks/clusters/", views.NksClusterListView.as_view(), name="nks-cluster-list"),
|
||||||
path("nks/clusters/create/", views.NksClusterCreateView.as_view(), name="nks-cluster-create"),
|
path("nks/clusters/create/", views.NksClusterCreateView.as_view(), name="nks-cluster-create"),
|
||||||
path("nks/clusters/<str:cluster_name>/", views.NksClusterDetailView.as_view(), name="nks-cluster-detail"),
|
path("nks/clusters/<str:cluster_name>/", views.NksClusterDetailView.as_view(), name="nks-cluster-detail"),
|
||||||
path("nks/clusters/<str:cluster_name>/config/", views.NksClusterConfigView.as_view(), name="nks-cluster-config"),
|
path("nks/clusters/<str:cluster_name>/config/", views.NksClusterConfigView.as_view(), name="nks-cluster-config"),
|
||||||
|
path("nks/clusters/<str:cluster_name>/resize/", views.NksClusterResizeView.as_view(), name="nks-cluster-resize"),
|
||||||
|
path("nks/clusters/<str:cluster_name>/upgrade/", views.NksClusterUpgradeView.as_view(), name="nks-cluster-upgrade"),
|
||||||
|
path("nks/clusters/<str:cluster_name>/certificates/", views.NksClusterCertificatesView.as_view(), name="nks-cluster-certificates"),
|
||||||
|
path("nks/clusters/<str:cluster_name>/ipacl/", views.NksClusterIpAclView.as_view(), name="nks-cluster-ipacl"),
|
||||||
|
|
||||||
|
# NKS Cluster Events
|
||||||
|
path("nks/clusters/<str:cluster_uuid>/events/", views.NksClusterEventsView.as_view(), name="nks-cluster-events"),
|
||||||
|
path("nks/clusters/<str:cluster_uuid>/events/<str:event_uuid>/", views.NksClusterEventDetailView.as_view(), name="nks-cluster-event-detail"),
|
||||||
|
|
||||||
|
# ==================== NKS Node Group ====================
|
||||||
|
path("nks/clusters/<str:cluster_name>/nodegroups/", views.NksNodeGroupListView.as_view(), name="nks-nodegroup-list"),
|
||||||
|
path("nks/clusters/<str:cluster_name>/nodegroups/create/", views.NksNodeGroupCreateView.as_view(), name="nks-nodegroup-create"),
|
||||||
|
path("nks/clusters/<str:cluster_name>/nodegroups/<str:nodegroup_name>/", views.NksNodeGroupDetailView.as_view(), name="nks-nodegroup-detail"),
|
||||||
|
path("nks/clusters/<str:cluster_name>/nodegroups/<str:nodegroup_name>/upgrade/", views.NksNodeGroupUpgradeView.as_view(), name="nks-nodegroup-upgrade"),
|
||||||
|
path("nks/clusters/<str:cluster_name>/nodegroups/<str:nodegroup_name>/userscript/", views.NksNodeGroupUserScriptView.as_view(), name="nks-nodegroup-userscript"),
|
||||||
|
path("nks/clusters/<str:cluster_name>/nodegroups/<str:nodegroup_name>/autoscale/", views.NksAutoscaleConfigView.as_view(), name="nks-nodegroup-autoscale"),
|
||||||
|
path("nks/clusters/<str:cluster_name>/nodegroups/<str:nodegroup_name>/start_node/", views.NksNodeStartView.as_view(), name="nks-node-start"),
|
||||||
|
path("nks/clusters/<str:cluster_name>/nodegroups/<str:nodegroup_name>/stop_node/", views.NksNodeStopView.as_view(), name="nks-node-stop"),
|
||||||
|
|
||||||
# ==================== Storage ====================
|
# ==================== Storage ====================
|
||||||
path("storage/containers/", views.StorageContainerListView.as_view(), name="storage-container-list"),
|
path("storage/containers/", views.StorageContainerListView.as_view(), name="storage-container-list"),
|
||||||
path("storage/containers/create/", views.StorageContainerCreateView.as_view(), name="storage-container-create"),
|
path("storage/containers/create/", views.StorageContainerCreateView.as_view(), name="storage-container-create"),
|
||||||
path("storage/containers/<str:container_name>/", views.StorageContainerDetailView.as_view(), name="storage-container-detail"),
|
path("storage/containers/<str:container_name>/", views.StorageContainerDetailView.as_view(), name="storage-container-detail"),
|
||||||
|
|
||||||
|
# ==================== DNS Plus - Zone ====================
|
||||||
|
path("dns/zones/", views.DnsZoneListView.as_view(), name="dns-zone-list"),
|
||||||
|
path("dns/zones/create/", views.DnsZoneCreateView.as_view(), name="dns-zone-create"),
|
||||||
|
path("dns/zones/<str:zone_id>/", views.DnsZoneDetailView.as_view(), name="dns-zone-detail"),
|
||||||
|
|
||||||
|
# ==================== DNS Plus - RecordSet ====================
|
||||||
|
path("dns/zones/<str:zone_id>/recordsets/", views.DnsRecordSetListView.as_view(), name="dns-recordset-list"),
|
||||||
|
path("dns/zones/<str:zone_id>/recordsets/create/", views.DnsRecordSetCreateView.as_view(), name="dns-recordset-create"),
|
||||||
|
path("dns/zones/<str:zone_id>/recordsets/<str:recordset_id>/", views.DnsRecordSetDetailView.as_view(), name="dns-recordset-detail"),
|
||||||
|
|
||||||
|
# ==================== DNS Plus - GSLB ====================
|
||||||
|
path("dns/gslbs/", views.DnsGslbListView.as_view(), name="dns-gslb-list"),
|
||||||
|
path("dns/gslbs/create/", views.DnsGslbCreateView.as_view(), name="dns-gslb-create"),
|
||||||
|
path("dns/gslbs/<str:gslb_id>/", views.DnsGslbDetailView.as_view(), name="dns-gslb-detail"),
|
||||||
|
path("dns/gslbs/<str:gslb_id>/pools/", views.DnsGslbPoolConnectView.as_view(), name="dns-gslb-pool-connection"),
|
||||||
|
|
||||||
|
# ==================== DNS Plus - Pool ====================
|
||||||
|
path("dns/pools/", views.DnsPoolListView.as_view(), name="dns-pool-list"),
|
||||||
|
path("dns/pools/create/", views.DnsPoolCreateView.as_view(), name="dns-pool-create"),
|
||||||
|
path("dns/pools/<str:pool_id>/", views.DnsPoolDetailView.as_view(), name="dns-pool-detail"),
|
||||||
|
|
||||||
|
# ==================== DNS Plus - Health Check ====================
|
||||||
|
path("dns/health-checks/", views.DnsHealthCheckListView.as_view(), name="dns-healthcheck-list"),
|
||||||
|
path("dns/health-checks/create/", views.DnsHealthCheckCreateView.as_view(), name="dns-healthcheck-create"),
|
||||||
|
path("dns/health-checks/<str:health_check_id>/", views.DnsHealthCheckDetailView.as_view(), name="dns-healthcheck-detail"),
|
||||||
]
|
]
|
||||||
|
|||||||
1128
nhn/views.py
1128
nhn/views.py
File diff suppressed because it is too large
Load Diff
@ -158,6 +158,7 @@ CORS_ALLOW_HEADERS = [
|
|||||||
"x-nhn-region",
|
"x-nhn-region",
|
||||||
"x-nhn-tenant-id",
|
"x-nhn-tenant-id",
|
||||||
"x-nhn-storage-account",
|
"x-nhn-storage-account",
|
||||||
|
"x-nhn-appkey",
|
||||||
]
|
]
|
||||||
|
|
||||||
# REST Framework settings
|
# REST Framework settings
|
||||||
|
|||||||
Reference in New Issue
Block a user