init: pcc module skeleton — models, CRUD, API, load_path
This commit is contained in:
commit
64a3dfd9e8
47
json/cluster_list.json
Normal file
47
json/cluster_list.json
Normal file
@ -0,0 +1,47 @@
|
||||
{
|
||||
"tblname": "cluster",
|
||||
"alias": "cluster_list",
|
||||
"title": "集群管理",
|
||||
"params": {
|
||||
"browserfields": {
|
||||
"name": {
|
||||
"title": "集群名称",
|
||||
"width": 150
|
||||
},
|
||||
"cluster_type": {
|
||||
"title": "集群类型",
|
||||
"width": 100
|
||||
},
|
||||
"pool_id": {
|
||||
"title": "算力池",
|
||||
"width": 150
|
||||
},
|
||||
"version": {
|
||||
"title": "版本",
|
||||
"width": 100
|
||||
},
|
||||
"endpoint": {
|
||||
"title": "入口",
|
||||
"width": 200
|
||||
},
|
||||
"status": {
|
||||
"title": "状态",
|
||||
"width": 100
|
||||
}
|
||||
},
|
||||
"editexclouded": [
|
||||
"id",
|
||||
"resellerid",
|
||||
"created_at",
|
||||
"updated_at"
|
||||
],
|
||||
"toolbar": {
|
||||
"tools": []
|
||||
},
|
||||
"binds": [],
|
||||
"new_data_url": "{{entire_url('/pcc/api/cluster_create.dspy')}}",
|
||||
"update_data_url": "{{entire_url('/pcc/api/cluster_update.dspy')}}",
|
||||
"delete_data_url": "{{entire_url('/pcc/api/cluster_delete.dspy')}}",
|
||||
"logined_userorgid": "resellerid"
|
||||
}
|
||||
}
|
||||
39
json/cluster_node_list.json
Normal file
39
json/cluster_node_list.json
Normal file
@ -0,0 +1,39 @@
|
||||
{
|
||||
"tblname": "cluster_node",
|
||||
"alias": "cluster_node_list",
|
||||
"title": "节点分配",
|
||||
"params": {
|
||||
"browserfields": {
|
||||
"cluster_id": {
|
||||
"title": "集群",
|
||||
"width": 150
|
||||
},
|
||||
"node_id": {
|
||||
"title": "节点",
|
||||
"width": 150
|
||||
},
|
||||
"role": {
|
||||
"title": "角色",
|
||||
"width": 100
|
||||
},
|
||||
"status": {
|
||||
"title": "状态",
|
||||
"width": 100
|
||||
}
|
||||
},
|
||||
"editexclouded": [
|
||||
"id",
|
||||
"resellerid",
|
||||
"created_at",
|
||||
"updated_at"
|
||||
],
|
||||
"toolbar": {
|
||||
"tools": []
|
||||
},
|
||||
"binds": [],
|
||||
"new_data_url": "{{entire_url('/cluster_node/api/cluster_node_create.dspy')}}",
|
||||
"update_data_url": "{{entire_url('/cluster_node/api/cluster_node_update.dspy')}}",
|
||||
"delete_data_url": "{{entire_url('/cluster_node/api/cluster_node_delete.dspy')}}",
|
||||
"logined_userorgid": "resellerid"
|
||||
}
|
||||
}
|
||||
116
models/cluster.json
Normal file
116
models/cluster.json
Normal file
@ -0,0 +1,116 @@
|
||||
{
|
||||
"summary": [
|
||||
{
|
||||
"name": "cluster",
|
||||
"title": "集群",
|
||||
"primary": [
|
||||
"id"
|
||||
],
|
||||
"catelog": "entity"
|
||||
}
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"name": "id",
|
||||
"title": "id",
|
||||
"type": "str",
|
||||
"length": 32,
|
||||
"nullable": "no"
|
||||
},
|
||||
{
|
||||
"name": "resellerid",
|
||||
"title": "商户机构id",
|
||||
"type": "str",
|
||||
"length": 32,
|
||||
"nullable": "no"
|
||||
},
|
||||
{
|
||||
"name": "pool_id",
|
||||
"title": "所属算力池",
|
||||
"type": "str",
|
||||
"length": 32
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"title": "集群名称",
|
||||
"type": "str",
|
||||
"length": 128,
|
||||
"nullable": "no"
|
||||
},
|
||||
{
|
||||
"name": "cluster_type",
|
||||
"title": "集群类型(k8s/slurm/ray)",
|
||||
"type": "char",
|
||||
"length": 16,
|
||||
"nullable": "no"
|
||||
},
|
||||
{
|
||||
"name": "version",
|
||||
"title": "版本号",
|
||||
"type": "str",
|
||||
"length": 64
|
||||
},
|
||||
{
|
||||
"name": "endpoint",
|
||||
"title": "API入口地址",
|
||||
"type": "str",
|
||||
"length": 256
|
||||
},
|
||||
{
|
||||
"name": "control_config",
|
||||
"title": "控制节点配置(JSON)",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"name": "compute_config",
|
||||
"title": "算力节点配置(JSON)",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"name": "status",
|
||||
"title": "状态(deploying/running/failed/stopped/destroyed)",
|
||||
"type": "char",
|
||||
"length": 16,
|
||||
"default": "deploying"
|
||||
},
|
||||
{
|
||||
"name": "deploy_log",
|
||||
"title": "部署日志(JSON)",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"name": "created_at",
|
||||
"title": "创建时间",
|
||||
"type": "timestamp",
|
||||
"nullable": "no"
|
||||
},
|
||||
{
|
||||
"name": "updated_at",
|
||||
"title": "更新时间",
|
||||
"type": "timestamp",
|
||||
"nullable": "no"
|
||||
}
|
||||
],
|
||||
"codes": [
|
||||
{
|
||||
"field": "pool_id",
|
||||
"table": "compute_pool",
|
||||
"valuefield": "id",
|
||||
"textfield": "name"
|
||||
},
|
||||
{
|
||||
"field": "cluster_type",
|
||||
"table": "appcodes_kv",
|
||||
"valuefield": "k",
|
||||
"textfield": "v",
|
||||
"cond": "parentid='cluster_type'"
|
||||
},
|
||||
{
|
||||
"field": "status",
|
||||
"table": "appcodes_kv",
|
||||
"valuefield": "k",
|
||||
"textfield": "v",
|
||||
"cond": "parentid='cluster_status'"
|
||||
}
|
||||
]
|
||||
}
|
||||
83
models/cluster_node.json
Normal file
83
models/cluster_node.json
Normal file
@ -0,0 +1,83 @@
|
||||
{
|
||||
"summary": [
|
||||
{
|
||||
"name": "cluster_node",
|
||||
"title": "集群节点分配",
|
||||
"primary": [
|
||||
"id"
|
||||
],
|
||||
"catelog": "entity"
|
||||
}
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"name": "id",
|
||||
"title": "id",
|
||||
"type": "str",
|
||||
"length": 32,
|
||||
"nullable": "no"
|
||||
},
|
||||
{
|
||||
"name": "cluster_id",
|
||||
"title": "所属集群",
|
||||
"type": "str",
|
||||
"length": 32,
|
||||
"nullable": "no"
|
||||
},
|
||||
{
|
||||
"name": "node_id",
|
||||
"title": "算力节点",
|
||||
"type": "str",
|
||||
"length": 32,
|
||||
"nullable": "no"
|
||||
},
|
||||
{
|
||||
"name": "role",
|
||||
"title": "角色(control/compute/storage)",
|
||||
"type": "char",
|
||||
"length": 16,
|
||||
"nullable": "no"
|
||||
},
|
||||
{
|
||||
"name": "status",
|
||||
"title": "状态(joining/active/draining/removed)",
|
||||
"type": "char",
|
||||
"length": 16,
|
||||
"default": "joining"
|
||||
},
|
||||
{
|
||||
"name": "assigned_at",
|
||||
"title": "分配时间",
|
||||
"type": "timestamp",
|
||||
"nullable": "no"
|
||||
}
|
||||
],
|
||||
"codes": [
|
||||
{
|
||||
"field": "cluster_id",
|
||||
"table": "cluster",
|
||||
"valuefield": "id",
|
||||
"textfield": "name"
|
||||
},
|
||||
{
|
||||
"field": "node_id",
|
||||
"table": "compute_node",
|
||||
"valuefield": "id",
|
||||
"textfield": "name"
|
||||
},
|
||||
{
|
||||
"field": "role",
|
||||
"table": "appcodes_kv",
|
||||
"valuefield": "k",
|
||||
"textfield": "v",
|
||||
"cond": "parentid='node_role'"
|
||||
},
|
||||
{
|
||||
"field": "status",
|
||||
"table": "appcodes_kv",
|
||||
"valuefield": "k",
|
||||
"textfield": "v",
|
||||
"cond": "parentid='node_status'"
|
||||
}
|
||||
]
|
||||
}
|
||||
211
pcc/__init__.py
Normal file
211
pcc/__init__.py
Normal file
@ -0,0 +1,211 @@
|
||||
"""
|
||||
pcc — 统一集群管理 (Pooled Computing Cluster)
|
||||
生命周期: deploy → run → stop → start → destroy
|
||||
插件: k8s_plugin, slurm_plugin, ray_plugin
|
||||
"""
|
||||
import datetime, json
|
||||
from appPublic.uniqueID import getID
|
||||
from sqlor.dbpools import DBPools
|
||||
|
||||
MODULE_NAME = 'pccs'
|
||||
|
||||
_PLUGINS = {
|
||||
'k8s': 'pcc.k8s_plugin',
|
||||
'slurm': 'pcc.slurm_plugin',
|
||||
'ray': 'pcc.ray_plugin',
|
||||
}
|
||||
|
||||
|
||||
def _get_plugin(cluster_type):
|
||||
import importlib
|
||||
modname = _PLUGINS.get(cluster_type)
|
||||
if not modname:
|
||||
raise ValueError(f'Unknown cluster_type: {cluster_type}')
|
||||
return importlib.import_module(modname)
|
||||
|
||||
|
||||
async def deploy_cluster(request, cluster_id):
|
||||
"""部署集群: 读取集群配置 → 分配节点 → 调用 plugin 部署"""
|
||||
env = request._run_ns
|
||||
dbname = env.get_module_dbname(MODULE_NAME)
|
||||
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
recs = await sor.R('cluster', {'id': cluster_id})
|
||||
if not recs:
|
||||
return {'status': 'error', 'message': f'Cluster {cluster_id} not found'}
|
||||
cluster = recs[0]
|
||||
|
||||
# 更新状态为 deploying
|
||||
await sor.U('cluster', {'id': cluster_id},
|
||||
{'status': 'deploying', 'updated_at': datetime.datetime.now().isoformat()})
|
||||
|
||||
# 获取已分配的节点
|
||||
node_recs = await sor.R('cluster_node', {'cluster_id': cluster_id})
|
||||
control_nodes = [await sor.R('compute_node', {'id': n.node_id}) for n in node_recs if n.role == 'control']
|
||||
compute_nodes = [await sor.R('compute_node', {'id': n.node_id}) for n in node_recs if n.role == 'compute']
|
||||
|
||||
control_nodes = [n[0] for n in control_nodes if n]
|
||||
compute_nodes = [n[0] for n in compute_nodes if n]
|
||||
|
||||
if not control_nodes:
|
||||
return {'status': 'error', 'message': 'No control node assigned'}
|
||||
|
||||
config = {}
|
||||
if cluster.control_config:
|
||||
try:
|
||||
config = json.loads(cluster.control_config)
|
||||
except:
|
||||
pass
|
||||
|
||||
# 调用对应 plugin 部署
|
||||
try:
|
||||
plugin = _get_plugin(cluster.cluster_type)
|
||||
deploy_log = await plugin.deploy_cluster(env, cluster_id, control_nodes, compute_nodes, config)
|
||||
except Exception as e:
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
await sor.U('cluster', {'id': cluster_id},
|
||||
{'status': 'failed', 'deploy_log': str(e),
|
||||
'updated_at': datetime.datetime.now().isoformat()})
|
||||
return {'status': 'error', 'message': f'Deploy failed: {e}'}
|
||||
|
||||
# 更新状态为 running
|
||||
endpoint = deploy_log.get('endpoint', '') if isinstance(deploy_log, dict) else ''
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
await sor.U('cluster', {'id': cluster_id},
|
||||
{'status': 'running', 'endpoint': endpoint,
|
||||
'deploy_log': json.dumps(deploy_log) if isinstance(deploy_log, dict) else str(deploy_log),
|
||||
'updated_at': datetime.datetime.now().isoformat()})
|
||||
# 更新节点状态为 active
|
||||
for n in control_nodes + compute_nodes:
|
||||
await sor.U('cluster_node', {'cluster_id': cluster_id, 'node_id': n.id},
|
||||
{'status': 'active'})
|
||||
|
||||
return {'status': 'ok', 'message': 'Cluster deployed', 'endpoint': endpoint}
|
||||
|
||||
|
||||
async def add_cluster_node(request, params_kw):
|
||||
"""向运行中集群动态添加节点"""
|
||||
env = request._run_ns
|
||||
dbname = env.get_module_dbname(MODULE_NAME)
|
||||
cluster_id = params_kw.get('cluster_id', '')
|
||||
node_id = params_kw.get('node_id', '')
|
||||
role = params_kw.get('role', 'compute')
|
||||
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
cluster = await sor.R('cluster', {'id': cluster_id})
|
||||
if not cluster:
|
||||
return {'status': 'error', 'message': 'Cluster not found'}
|
||||
cluster = cluster[0]
|
||||
|
||||
node = await sor.R('compute_node', {'id': node_id})
|
||||
if not node:
|
||||
return {'status': 'error', 'message': 'Node not found'}
|
||||
node = node[0]
|
||||
|
||||
# 更新节点状态
|
||||
await sor.U('compute_node', {'id': node_id},
|
||||
{'status': 'allocated', 'updated_at': datetime.datetime.now().isoformat()})
|
||||
|
||||
# 记录分配
|
||||
await sor.C('cluster_node', {
|
||||
'id': getID(), 'cluster_id': cluster_id, 'node_id': node_id,
|
||||
'role': role, 'status': 'joining',
|
||||
'assigned_at': datetime.datetime.now().isoformat()
|
||||
})
|
||||
|
||||
# 调用 plugin 添加
|
||||
plugin = _get_plugin(cluster.cluster_type)
|
||||
result = await plugin.add_node(env, cluster_id, node_id, role)
|
||||
return result
|
||||
|
||||
|
||||
async def remove_cluster_node(request, params_kw):
|
||||
"""从集群移除节点并回收"""
|
||||
env = request._run_ns
|
||||
dbname = env.get_module_dbname(MODULE_NAME)
|
||||
cluster_id = params_kw.get('cluster_id', '')
|
||||
node_id = params_kw.get('node_id', '')
|
||||
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
cluster = await sor.R('cluster', {'id': cluster_id})
|
||||
if not cluster:
|
||||
return {'status': 'error', 'message': 'Cluster not found'}
|
||||
|
||||
plugin = _get_plugin(cluster[0].cluster_type)
|
||||
result = await plugin.remove_node(env, cluster_id, node_id)
|
||||
|
||||
# 回收节点
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
await sor.U('compute_node', {'id': node_id},
|
||||
{'status': 'available', 'updated_at': datetime.datetime.now().isoformat()})
|
||||
await sor.U('cluster_node', {'cluster_id': cluster_id, 'node_id': node_id},
|
||||
{'status': 'removed'})
|
||||
|
||||
return result
|
||||
|
||||
|
||||
async def cluster_status(request, cluster_id):
|
||||
"""获取集群运行状态"""
|
||||
env = request._run_ns
|
||||
dbname = env.get_module_dbname(MODULE_NAME)
|
||||
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
cluster = await sor.R('cluster', {'id': cluster_id})
|
||||
if not cluster:
|
||||
return {'status': 'error', 'message': 'Cluster not found'}
|
||||
cluster = cluster[0]
|
||||
nodes = await sor.R('cluster_node', {'cluster_id': cluster_id})
|
||||
|
||||
# 从 plugin 获取实时状态
|
||||
try:
|
||||
plugin = _get_plugin(cluster.cluster_type)
|
||||
live_status = await plugin.cluster_status(env, cluster_id)
|
||||
except:
|
||||
live_status = {}
|
||||
|
||||
return {
|
||||
'status': 'ok',
|
||||
'data': {
|
||||
'id': cluster.id, 'name': cluster.name, 'type': cluster.cluster_type,
|
||||
'version': cluster.version, 'endpoint': cluster.endpoint,
|
||||
'state': cluster.status,
|
||||
'nodes': [{'id': n.node_id, 'role': n.role, 'status': n.status} for n in nodes],
|
||||
'live': live_status
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async def stop_cluster(request, cluster_id):
|
||||
"""停止集群"""
|
||||
env = request._run_ns
|
||||
dbname = env.get_module_dbname(MODULE_NAME)
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
await sor.U('cluster', {'id': cluster_id},
|
||||
{'status': 'stopped', 'updated_at': datetime.datetime.now().isoformat()})
|
||||
return {'status': 'ok', 'message': 'Cluster stopped'}
|
||||
|
||||
|
||||
async def start_cluster(request, cluster_id):
|
||||
"""启动已停止的集群"""
|
||||
env = request._run_ns
|
||||
dbname = env.get_module_dbname(MODULE_NAME)
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
await sor.U('cluster', {'id': cluster_id},
|
||||
{'status': 'running', 'updated_at': datetime.datetime.now().isoformat()})
|
||||
return {'status': 'ok', 'message': 'Cluster started'}
|
||||
|
||||
|
||||
async def destroy_cluster(request, cluster_id):
|
||||
"""销毁集群,回收所有节点"""
|
||||
env = request._run_ns
|
||||
dbname = env.get_module_dbname(MODULE_NAME)
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
# 释放所有节点
|
||||
nodes = await sor.R('cluster_node', {'cluster_id': cluster_id})
|
||||
for n in nodes:
|
||||
await sor.U('compute_node', {'id': n.node_id},
|
||||
{'status': 'available', 'updated_at': datetime.datetime.now().isoformat()})
|
||||
await sor.U('cluster_node', {'id': n.id}, {'status': 'removed'})
|
||||
await sor.U('cluster', {'id': cluster_id},
|
||||
{'status': 'destroyed', 'updated_at': datetime.datetime.now().isoformat()})
|
||||
return {'status': 'ok', 'message': 'Cluster destroyed'}
|
||||
24
pcc/k8s_plugin/__init__.py
Normal file
24
pcc/k8s_plugin/__init__.py
Normal file
@ -0,0 +1,24 @@
|
||||
'''
|
||||
k8s_plugin - K8s + VM 集群管理
|
||||
- K8s 部署 (kubeadm/k3s + 控制平面 + worker)
|
||||
- VM 生命周期 (创建/快照/迁移/销毁)
|
||||
- 节点加入/退出集群
|
||||
- kubectl / k8s API 状态监控
|
||||
- 算力单元: namespace + ResourceQuota 分配回收
|
||||
''')
|
||||
|
||||
async def deploy_k8s_cluster(env, cluster_id, control_nodes, compute_nodes, config):
|
||||
'''部署 K8s 集群: 初始化控制平面 → join worker → 验证'''
|
||||
pass
|
||||
|
||||
async def add_node(env, cluster_id, node_id, role='compute'):
|
||||
'''向已有集群添加节点'''
|
||||
pass
|
||||
|
||||
async def remove_node(env, cluster_id, node_id):
|
||||
'''从集群移除节点并回收资源'''
|
||||
pass
|
||||
|
||||
async def cluster_status(env, cluster_id):
|
||||
'''获取集群状态 (节点数、Pod数、资源使用率)'''
|
||||
pass
|
||||
23
pcc/ray_plugin/__init__.py
Normal file
23
pcc/ray_plugin/__init__.py
Normal file
@ -0,0 +1,23 @@
|
||||
'''
|
||||
ray_plugin - Ray 集群管理
|
||||
- Ray 集群部署 (ray start --head / --worker)
|
||||
- 自动扩缩容 (Ray Autoscaler)
|
||||
- Dashboard + Prometheus 监控
|
||||
- 算力单元: Ray resource 标签分配回收
|
||||
'''
|
||||
|
||||
async def deploy_ray_cluster(env, cluster_id, control_nodes, compute_nodes, config):
|
||||
'''部署 Ray 集群: head node → worker nodes'''
|
||||
pass
|
||||
|
||||
async def add_node(env, cluster_id, node_id, role='compute'):
|
||||
'''向 Ray 集群添加 worker'''
|
||||
pass
|
||||
|
||||
async def remove_node(env, cluster_id, node_id):
|
||||
'''从 Ray 集群移除 worker'''
|
||||
pass
|
||||
|
||||
async def cluster_status(env, cluster_id):
|
||||
'''Ray Dashboard API 状态'''
|
||||
pass
|
||||
24
pcc/slurm_plugin/__init__.py
Normal file
24
pcc/slurm_plugin/__init__.py
Normal file
@ -0,0 +1,24 @@
|
||||
'''
|
||||
slurm_plugin - Slurm 集群管理
|
||||
- Slurm 部署 (slurmctld + slurmd + munge + 共享存储)
|
||||
- Partition 管理 / QoS / TRES
|
||||
- 节点 drain/resume (算力回收/分配)
|
||||
- sinfo / squeue / scontrol 监控
|
||||
'''
|
||||
'')
|
||||
|
||||
async def deploy_slurm_cluster(env, cluster_id, control_nodes, compute_nodes, config):
|
||||
'''部署 Slurm 集群: 控制节点 slurmctld + munge, 算力节点 slurmd'''
|
||||
pass
|
||||
|
||||
async def add_node(env, cluster_id, node_id, role='compute'):
|
||||
'''向分区添加节点'''
|
||||
pass
|
||||
|
||||
async def remove_node(env, cluster_id, node_id):
|
||||
'''drain 节点后从集群移除'''
|
||||
pass
|
||||
|
||||
async def cluster_status(env, cluster_id):
|
||||
'''sinfo + squeue 汇总'''
|
||||
pass
|
||||
68
scripts/load_path.py
Normal file
68
scripts/load_path.py
Normal file
@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env python3
|
||||
"""pcc RBAC 权限管理"""
|
||||
import subprocess, os, sys, json, glob
|
||||
|
||||
def find_sage_root():
|
||||
for c in [os.path.expanduser("~/sage"), os.path.expanduser("~/repos/sage")]:
|
||||
if os.path.isdir(os.path.join(c, "py3")): return c
|
||||
return None
|
||||
|
||||
SAGE = find_sage_root()
|
||||
if not SAGE: sys.exit("Sage root not found")
|
||||
PY = os.path.join(SAGE, "py3", "bin", "python")
|
||||
SET = os.path.join(SAGE, "set_role_perm.py")
|
||||
|
||||
JSON_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "json")
|
||||
API_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "wwwroot", "api")
|
||||
|
||||
def load_cruds():
|
||||
defs = []
|
||||
for f in sorted(glob.glob(os.path.join(JSON_DIR, "*.json"))):
|
||||
try:
|
||||
d = json.load(open(f))
|
||||
defs.append({"tblname": d["tblname"], "alias": d.get("alias", d["tblname"]), "subtables": d.get("params", {}).get("subtables", [])})
|
||||
except: pass
|
||||
return defs
|
||||
|
||||
def get_apis():
|
||||
if not os.path.isdir(API_DIR): return []
|
||||
return [f"/pcc/api/{f}" for f in sorted(os.listdir(API_DIR)) if f.endswith(".dspy")]
|
||||
|
||||
cruds = load_cruds()
|
||||
apis = get_apis()
|
||||
|
||||
PATHS_ANY = [
|
||||
f"/pcc/menu.ui",
|
||||
]
|
||||
PATHS_LOGINED = [
|
||||
f"/pcc",
|
||||
f"/pcc/index.ui",
|
||||
]
|
||||
for d in cruds:
|
||||
PATHS_ANY.append(f"/pcc/{d['alias']}")
|
||||
PATHS_LOGINED.append(f"/pcc/{d['alias']}/index.ui")
|
||||
for act in ["get", "add", "update", "delete"]:
|
||||
PATHS_LOGINED.append(f"/pcc/{d['alias']}/{act}_{d['tblname']}.dspy")
|
||||
for api in apis:
|
||||
PATHS_LOGINED.append(api)
|
||||
|
||||
PATHS_OPERATOR = list(PATHS_LOGINED)
|
||||
|
||||
PATHS_ANY = list(dict.fromkeys(PATHS_ANY))
|
||||
PATHS_LOGINED = list(dict.fromkeys(PATHS_LOGINED))
|
||||
PATHS_OPERATOR = list(dict.fromkeys(PATHS_OPERATOR))
|
||||
|
||||
def reg(role, paths):
|
||||
ok = 0
|
||||
for p in paths:
|
||||
r = subprocess.run([PY, SET, role, p], capture_output=True, text=True)
|
||||
if r.returncode == 0: ok += 1
|
||||
print(f" {role}: {ok}/{len(paths)}")
|
||||
return ok
|
||||
|
||||
total = 0
|
||||
print(f"{mod_name}: any={len(PATHS_ANY)} logined={len(PATHS_LOGINED)} operator={len(PATHS_OPERATOR)}")
|
||||
total += reg("any", PATHS_ANY)
|
||||
total += reg("logined", PATHS_LOGINED)
|
||||
total += reg("reseller.operator", PATHS_OPERATOR)
|
||||
print(f"Done. {total} entries.")
|
||||
8
setup.json
Normal file
8
setup.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "pcc",
|
||||
"version": "0.1.0",
|
||||
"description": "统一集群管理:K8s/Slurm/Ray 集群生命周期、节点分配、动态维护、监控",
|
||||
"packages": ["pcc", "pcc.k8s_plugin", "pcc.slurm_plugin", "pcc.ray_plugin"],
|
||||
"install_requires": ["apppublic", "sqlor", "ahserver", "appbase", "rbac"],
|
||||
"python_requires": ">=3.10"
|
||||
}
|
||||
3
wwwroot/api/cluster_add_node.dspy
Normal file
3
wwwroot/api/cluster_add_node.dspy
Normal file
@ -0,0 +1,3 @@
|
||||
# 向运行中集群动态添加节点
|
||||
result = await add_cluster_node(request, params_kw)
|
||||
return result if isinstance(result, dict) else {'widgettype': 'Error', 'options': {'title': '失败', 'message': str(result)}}
|
||||
3
wwwroot/api/cluster_create.dspy
Normal file
3
wwwroot/api/cluster_create.dspy
Normal file
@ -0,0 +1,3 @@
|
||||
# cluster create
|
||||
result = {'status': 'ok', 'message': 'cluster created'}
|
||||
return result
|
||||
3
wwwroot/api/cluster_delete.dspy
Normal file
3
wwwroot/api/cluster_delete.dspy
Normal file
@ -0,0 +1,3 @@
|
||||
# cluster delete
|
||||
result = {'status': 'ok', 'message': 'cluster deleted'}
|
||||
return result
|
||||
7
wwwroot/api/cluster_deploy.dspy
Normal file
7
wwwroot/api/cluster_deploy.dspy
Normal file
@ -0,0 +1,7 @@
|
||||
# 部署集群:分配控制节点 + 算力节点 → 调用对应 plugin 部署
|
||||
cluster_id = params_kw.get('cluster_id', '')
|
||||
if not cluster_id:
|
||||
return {'widgettype': 'Error', 'options': {'title': '失败', 'message': '缺少集群ID'}}
|
||||
|
||||
result = await deploy_cluster(request, cluster_id)
|
||||
return result if isinstance(result, dict) else {'widgettype': 'Error', 'options': {'title': '失败', 'message': str(result)}}
|
||||
4
wwwroot/api/cluster_destroy.dspy
Normal file
4
wwwroot/api/cluster_destroy.dspy
Normal file
@ -0,0 +1,4 @@
|
||||
# 销毁集群并回收所有节点
|
||||
cluster_id = params_kw.get('cluster_id', '')
|
||||
result = await destroy_cluster(request, cluster_id)
|
||||
return result
|
||||
3
wwwroot/api/cluster_node_create.dspy
Normal file
3
wwwroot/api/cluster_node_create.dspy
Normal file
@ -0,0 +1,3 @@
|
||||
# cluster_node create
|
||||
result = {'status': 'ok', 'message': 'cluster_node created'}
|
||||
return result
|
||||
3
wwwroot/api/cluster_node_delete.dspy
Normal file
3
wwwroot/api/cluster_node_delete.dspy
Normal file
@ -0,0 +1,3 @@
|
||||
# cluster_node delete
|
||||
result = {'status': 'ok', 'message': 'cluster_node deleted'}
|
||||
return result
|
||||
3
wwwroot/api/cluster_node_update.dspy
Normal file
3
wwwroot/api/cluster_node_update.dspy
Normal file
@ -0,0 +1,3 @@
|
||||
# cluster_node update
|
||||
result = {'status': 'ok', 'message': 'cluster_node updated'}
|
||||
return result
|
||||
3
wwwroot/api/cluster_remove_node.dspy
Normal file
3
wwwroot/api/cluster_remove_node.dspy
Normal file
@ -0,0 +1,3 @@
|
||||
# 从集群移除节点
|
||||
result = await remove_cluster_node(request, params_kw)
|
||||
return result if isinstance(result, dict) else {'widgettype': 'Error', 'options': {'title': '失败', 'message': str(result)}}
|
||||
4
wwwroot/api/cluster_start.dspy
Normal file
4
wwwroot/api/cluster_start.dspy
Normal file
@ -0,0 +1,4 @@
|
||||
# 启动集群
|
||||
cluster_id = params_kw.get('cluster_id', '')
|
||||
result = await start_cluster(request, cluster_id)
|
||||
return result
|
||||
6
wwwroot/api/cluster_status.dspy
Normal file
6
wwwroot/api/cluster_status.dspy
Normal file
@ -0,0 +1,6 @@
|
||||
# 获取集群运行状态
|
||||
cluster_id = params_kw.get('cluster_id', '')
|
||||
if not cluster_id:
|
||||
return {'status': 'error', 'message': 'Missing cluster_id'}
|
||||
result = await cluster_status(request, cluster_id)
|
||||
return result
|
||||
4
wwwroot/api/cluster_stop.dspy
Normal file
4
wwwroot/api/cluster_stop.dspy
Normal file
@ -0,0 +1,4 @@
|
||||
# 停止集群
|
||||
cluster_id = params_kw.get('cluster_id', '')
|
||||
result = await stop_cluster(request, cluster_id)
|
||||
return result
|
||||
3
wwwroot/api/cluster_update.dspy
Normal file
3
wwwroot/api/cluster_update.dspy
Normal file
@ -0,0 +1,3 @@
|
||||
# cluster update
|
||||
result = {'status': 'ok', 'message': 'cluster updated'}
|
||||
return result
|
||||
Loading…
x
Reference in New Issue
Block a user