├── vmid.json
├── cookie
└── ikuai.json
├── ip_pool.json
├── port_pool.json
├── img
├── 信息页.png
├── 前台订单页.png
├── 快照页.png
├── 操作记录页.png
├── 磁盘页.png
├── 网络页.png
├── 远程页.png
├── 前台控制台页.png
├── 后台商品配置页.png
├── 端口映射页面-0.png
└── 端口映射页面-1.png
├── templates
├── error.html
├── disk.html
├── kzt.html
├── rw.html
├── network.html
├── connect.html
├── demo.html
├── nat.html
├── snapshot.html
└── info.html
├── README.md
└── proxmoxlxc.php
/vmid.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/cookie/ikuai.json:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ip_pool.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/port_pool.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/img/信息页.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MasonDye/ProxmoxVE-LXC-X-module/HEAD/img/信息页.png
--------------------------------------------------------------------------------
/img/前台订单页.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MasonDye/ProxmoxVE-LXC-X-module/HEAD/img/前台订单页.png
--------------------------------------------------------------------------------
/img/快照页.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MasonDye/ProxmoxVE-LXC-X-module/HEAD/img/快照页.png
--------------------------------------------------------------------------------
/img/操作记录页.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MasonDye/ProxmoxVE-LXC-X-module/HEAD/img/操作记录页.png
--------------------------------------------------------------------------------
/img/磁盘页.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MasonDye/ProxmoxVE-LXC-X-module/HEAD/img/磁盘页.png
--------------------------------------------------------------------------------
/img/网络页.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MasonDye/ProxmoxVE-LXC-X-module/HEAD/img/网络页.png
--------------------------------------------------------------------------------
/img/远程页.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MasonDye/ProxmoxVE-LXC-X-module/HEAD/img/远程页.png
--------------------------------------------------------------------------------
/img/前台控制台页.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MasonDye/ProxmoxVE-LXC-X-module/HEAD/img/前台控制台页.png
--------------------------------------------------------------------------------
/img/后台商品配置页.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MasonDye/ProxmoxVE-LXC-X-module/HEAD/img/后台商品配置页.png
--------------------------------------------------------------------------------
/img/端口映射页面-0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MasonDye/ProxmoxVE-LXC-X-module/HEAD/img/端口映射页面-0.png
--------------------------------------------------------------------------------
/img/端口映射页面-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MasonDye/ProxmoxVE-LXC-X-module/HEAD/img/端口映射页面-1.png
--------------------------------------------------------------------------------
/templates/error.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
13 |
14 |
6 |
7 |
8 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | | 快照名称 |
25 | 创建日期/状态 |
26 | 描述 |
27 | 操作 |
28 |
29 |
30 |
31 | {foreach $snapshot as $key => $vo}
32 |
33 | |
34 | {$vo.name}
35 | |
36 | {$vo.snaptime} |
37 | {$vo.description} |
38 |
39 |
40 |
44 |
49 |
50 | |
51 |
52 | {/foreach}
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
248 |
--------------------------------------------------------------------------------
/templates/info.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
处理器使用率
44 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
81 |
104 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
153 |
154 |
155 |
--------------------------------------------------------------------------------
/proxmoxlxc.php:
--------------------------------------------------------------------------------
1 | 'ProxmoxVE-LXC对接模块', 'APIVersion'=>'2.0', 'HelpDoc'=>'https://www.almondnet.cn/'];
10 | }
11 |
12 | // 测试链接
13 | function proxmoxlxc_TestLink($params){
14 |
15 | // 构建URL
16 |
17 | // $url = $params['server_http_prefix']."://".$params['server_ip'].":".$params['port'];
18 | // return $url;
19 |
20 | $info = json_decode(proxmoxlxc_request($params,"/api2/json/nodes/".$params['hostname']."/status"),true);
21 |
22 | if($info==null || $info['data']==null){
23 | $result['status'] = 200;
24 | $result['data']['server_status'] = 0;
25 | $result['data']['msg'] ="无法连接,地址可能错误或者是密钥不正确".json_encode($info);
26 | return $result;
27 | }else{
28 | $result['status'] = 200;
29 | $result['data']['server_status'] = 1;
30 | $result['data']['msg'] = json_encode($info);
31 | return $result;
32 | }
33 |
34 | // return
35 |
36 | }
37 |
38 | // 后台设置输出内容
39 | function proxmoxlxc_ConfigOptions(){
40 | return [
41 | [
42 | 'type'=>'text',
43 | 'name'=>'系统网卡名称',
44 | 'description'=>'分配给虚拟机的网卡',
45 | 'placeholder'=>'vmbr0',
46 | 'default'=>"vmbr0",
47 | 'key'=>'net_name'
48 | ],
49 | [
50 | 'type'=>'text',
51 | 'name'=>'处理器限制',
52 | 'description'=>'处理器性能百分比限制(0无限制)',
53 | 'placeholder'=>'0-8192 (0无限制)',
54 | 'default'=>"0",
55 | 'key'=>'cpulimit'
56 | ],
57 | [
58 | 'type'=>'text',
59 | 'name'=>'处理器权重',
60 | 'description'=>'越大此机器获取的CPU时间越长(0禁用)',
61 | 'placeholder'=>'0-500000 (0禁用)',
62 | 'default'=>"0",
63 | 'key'=>'cpuunits'
64 | ],[
65 | 'type'=>'text',
66 | 'name'=>'IP地址池',
67 | 'description'=>'IPV4地址范围(逗号隔开)',
68 | 'placeholder'=>'172.16.0.2,172.16.0.20',
69 | 'default'=>"172.16.0.2,172.16.0.20",
70 | 'key'=>'ip_pool'
71 | ],[
72 | 'type'=>'text',
73 | 'name'=>'掩码',
74 | 'description'=>'掩码地址',
75 | 'placeholder'=>'24',
76 | 'default'=>"24",
77 | 'key'=>'Mask'
78 | ],[
79 | 'type'=>'text',
80 | 'name'=>'IP网关',
81 | 'description'=>'网关',
82 | 'placeholder'=>'172.16.0.1',
83 | 'default'=>"172.16.0.1",
84 | 'key'=>'gateway'
85 | ],[
86 | 'type'=>'text',
87 | 'name'=>'DNS服务器',
88 | 'description'=>'域名解析服务器地址',
89 | 'placeholder'=>'8.8.8.8',
90 | 'default'=>"8.8.8.8",
91 | 'key'=>'dns'
92 | ],[
93 | 'type'=>'text',
94 | 'name'=>'系统盘存放盘',
95 | 'description'=>'存放系统盘的盘名',
96 | 'placeholder'=>'local-lvm',
97 | 'default'=>"local-lvm",
98 | 'key'=>'system_disk'
99 | ],
100 | [
101 | 'type'=>'dropdown',
102 | 'name'=>'交换内存',
103 | 'description'=>'按照需求和定位酌情分配',
104 | 'options'=>[
105 | 'noswap'=>'不分配',
106 | '1024'=>'1024',
107 | '1:1'=>'对等分配'
108 | ],
109 | 'default'=>"noswap",
110 | 'key'=>'swap'
111 | ],
112 | [
113 | 'type'=>'dropdown',
114 | 'name'=>'类型',
115 | 'description'=>'NAT 或 标准',
116 | 'options'=>[
117 | 'bz'=>'标准',
118 | 'nat'=>'nat',
119 | ],
120 | 'default'=>"bz",
121 | 'key'=>'nat'
122 | ],[
123 | 'type'=>'text',
124 | 'name'=>'爱快地址',
125 | 'description'=>'请包含请求头加端口',
126 | 'placeholder'=>'http://example.com:80',
127 | 'default'=>"http://example.com:80",
128 | 'key'=>'ikuai_url'
129 | ],[
130 | 'type'=>'text',
131 | 'name'=>'爱快用户名',
132 | 'description'=>'类型为标准模式勿略',
133 | 'placeholder'=>'admin',
134 | 'default'=>"admin",
135 | 'key'=>'ikuai_username'
136 | ],[
137 | 'type'=>'text',
138 | 'name'=>'爱快密码',
139 | 'description'=>'类型为标准模式勿略',
140 | 'placeholder'=>'password',
141 | 'default'=>"password",
142 | 'key'=>'ikuai_password'
143 | ],[
144 | 'type'=>'text',
145 | 'name'=>'映射展示地址',
146 | 'description'=>'类型为标准模式勿略',
147 | 'placeholder'=>'example.com',
148 | 'default'=>"example.com",
149 | 'key'=>'ikuai_ip'
150 | ],[
151 | 'type'=>'text',
152 | 'name'=>'端口池-开始',
153 | 'description'=>'端口范围(开始)',
154 | 'placeholder'=>'40000',
155 | 'default'=>"40000",
156 | 'key'=>'port_pool_start'
157 | ],[
158 | 'type'=>'text',
159 | 'name'=>'VMID起始值',
160 | 'description'=>'VMID起始值,唯一值不能相同',
161 | 'placeholder'=>'500',
162 | 'default'=>"500",
163 | 'key'=>'vmid_start'
164 | ],[
165 | 'type'=>'text',
166 | 'name'=>'产品唯一值',
167 | 'description'=>'产品唯一值,不能相同,推荐UUID',
168 | 'placeholder'=>'D6D58A71-BA11-9192-E822-B2F46EBF1C65',
169 | 'default'=>"D6D58A71-BA11-9192-E822-B2F46EBF1C65",
170 | 'key'=>'product_unique_value'
171 | ],[
172 | 'type'=>'text',
173 | 'name'=>'端口池-结束',
174 | 'description'=>'端口范围(结束)',
175 | 'placeholder'=>'50000',
176 | 'default'=>"50000",
177 | 'key'=>'port_pool_end'
178 | ],[
179 | 'type'=>'dropdown',
180 | 'name'=>'嵌套虚拟化',
181 | 'description'=>'Docker等虚拟化是否允许',
182 | 'options'=>[
183 | 'open'=>'开启',
184 | 'close'=>'关闭',
185 | ],
186 | 'default'=>"open",
187 | 'key'=>'nesting'
188 | ],
189 |
190 |
191 | ];
192 |
193 | }
194 |
195 |
196 | // 前台页面
197 | function proxmoxlxc_ClientArea($params){
198 |
199 | $info = [
200 | 'info'=>['name'=>'信息'],
201 | 'net'=>['name'=>'网络'],
202 | 'disk'=>['name'=>'硬盘'],
203 | // 'firewalld'=>['name'=>'防火墙'],
204 | 'snapshot'=>['name'=>'快照'],
205 | 'connect'=>['name'=>'远程连接'],
206 | // 'demo'=>['name'=>'测试'],
207 | ];
208 | if($params['configoptions']['nat']=='nat'){
209 | $info['nat']=['name'=>'端口映射'];
210 | }
211 |
212 | $info['rw']=['name'=>'操作记录'];
213 |
214 | return $info;
215 | }
216 |
217 |
218 | // 前台输出页面
219 | function proxmoxlxc_ClientAreaOutput($params, $key){
220 |
221 | if($key == "demo"){
222 |
223 | // 信息页面
224 | return [
225 | 'template'=>'templates/demo.html',
226 | 'vars'=>[
227 | 'params'=>$params
228 | ]
229 | ];
230 |
231 | }
232 |
233 | if($key == "info"){
234 |
235 | // 信息页面
236 | return [
237 | 'template'=>'templates/info.html',
238 | 'vars'=>[
239 | 'params'=>$params
240 | ]
241 | ];
242 |
243 | }else if($key == "net"){
244 | // 网络页面
245 | return [
246 | 'template'=>'templates/network.html',
247 | 'vars'=>[
248 | 'params'=>$params,
249 | 'test'=>json_encode($params),
250 | 'network'=>proxmoxlxc_GET_lxc_config($params)['data']['net0']
251 | ]
252 | ];
253 |
254 | }else if($key == "disk"){
255 | // 磁盘页面
256 | return [
257 | 'template'=>'templates/disk.html',
258 | 'vars'=>[
259 | 'params'=>$params,
260 | 'temp'=>json_encode(proxmoxlxc_GET_lxc_config($params)['data']),
261 | 'disk'=>proxmoxlxc_GET_lxc_config($params)['data']['rootfs']
262 | ]
263 | ];
264 |
265 | }else if($key == "snapshot"){
266 | // 快照页面
267 | return [
268 | 'template'=>'templates/snapshot.html',
269 | 'vars'=>[
270 | 'params'=>$params,
271 | 'temp'=>json_encode(proxmoxlxc_GET_lxc_snapshot_list($params)),
272 | 'snapshot'=>proxmoxlxc_GET_lxc_snapshot_list($params)
273 |
274 | ]
275 | ];
276 |
277 | }else if($key == "rw"){
278 | // 任务页面
279 | return [
280 | 'template'=>'templates/rw.html',
281 | 'vars'=>[
282 | 'params'=>$params,
283 | 'temp'=>json_encode(proxmoxlxc_tasks_get_list($params)),
284 | 'tasks'=>proxmoxlxc_tasks_get_list($params)
285 |
286 | ]
287 | ];
288 |
289 | }else if($key == "nat"){
290 | // 映射页面
291 | // proxmoxlxc_nat_request($params,"","");
292 | // 获取映射列表
293 |
294 | $nat_list = proxmoxlxc_nat_get_list($params);
295 |
296 | // $port_pool = explode(',', $params['configoptions']['port_pool_opt']);
297 | // $min_port = (int)$port_pool[0];
298 | // $max_port = (int)$port_pool[1];
299 |
300 | $min_port = (int)$params['configoptions']['port_pool_start'];
301 | $max_port = (int)$params['configoptions']['port_pool_end'];
302 |
303 | if($nat_list['ErrMsg']=='Success'){
304 | // return "登陆成功了 本币";
305 | // return $info;
306 | // echo json_encode($nat_list);
307 |
308 | return [
309 | 'template'=>'templates/nat.html',
310 | 'vars'=>[
311 | 'params'=>$params,
312 | 'list'=>$nat_list['Data'],
313 | 'test'=>json_encode($params),
314 | 'max_port'=>$max_port,
315 | 'min_port'=>$min_port
316 | // 'temp'=>json_encode(proxmoxlxc_tasks_get_list($params)),
317 | // 'tasks'=>proxmoxlxc_tasks_get_list($params)
318 |
319 | ]
320 | ];
321 |
322 |
323 | }else{
324 | // return json_encode($info);
325 | return [
326 | 'template'=>'templates/error.html',
327 | 'vars'=>[
328 | 'error'=>[
329 | 'code'=>'502',
330 | 'msg'=>'无法连接映射服务器',
331 | 'info'=>$nat_list['ErrMsg']
332 | ]
333 | ]
334 | ];
335 | }
336 |
337 |
338 |
339 | }else if($key == "connect"){
340 | // 任务页面
341 | // $ticket = proxmoxlxc_get_ticket($params);
342 | $port ="null";
343 |
344 | if($params['configoptions']['nat']=='nat'){
345 | $nat_list = proxmoxlxc_nat_get_list($params);
346 |
347 | if($nat_list['ErrMsg']=='Success'){
348 |
349 | foreach ($nat_list['Data']['data'] as $value){
350 | if($value['lan_port']=='22'){
351 | $port = $value['wan_port'];
352 | }
353 | }
354 |
355 | }
356 |
357 | }else{
358 | $port = '22';
359 | }
360 |
361 | return [
362 | 'template'=>'templates/connect.html',
363 | 'vars'=>[
364 | 'params'=>$params,
365 | 'port'=>$port,
366 | 'vnc'=>proxmoxlxc_vnc_if($params)
367 |
368 | ]
369 | ];
370 |
371 | }
372 |
373 | }
374 |
375 | // 允许执行的func
376 | function proxmoxlxc_AllowFunction(){
377 | return [
378 | 'client'=>["Getcurrent","delete_snapshot","RollBACK_snapshot","create_snapshot","nat_add","nat_del","Vnc"],
379 | ];
380 | }
381 |
382 | /*图表*/
383 |
384 |
385 | // 定义图表
386 | function proxmoxlxc_Chart(){
387 | return [
388 | 'cpu'=>[ // 图表type
389 | 'title'=>'CPU使用率', // 标题
390 | 'select'=>[
391 | [
392 | 'name'=>'1小时',
393 | 'value'=>'hour'
394 | ],
395 | [
396 | 'name'=>'一天',
397 | 'value'=>'day'
398 |
399 | ],
400 | [
401 | 'name'=>'七天',
402 | 'value'=>'week'
403 | ],[
404 | 'name'=>'一月',
405 | 'value'=>'month'
406 | ],
407 | ]
408 |
409 |
410 | ],
411 | 'mem'=>[ // 图表type
412 | 'title'=>'内存使用率', // 标题
413 | 'select'=>[
414 | [
415 | 'name'=>'1小时',
416 | 'value'=>'hour'
417 | ],
418 | [
419 | 'name'=>'一天',
420 | 'value'=>'day'
421 |
422 | ],
423 | [
424 | 'name'=>'七天',
425 | 'value'=>'week'
426 | ],[
427 | 'name'=>'一月',
428 | 'value'=>'month'
429 | ],
430 | ]
431 |
432 | ],
433 | 'disk'=>[ // 图表type
434 | 'title'=>'硬盘IO', // 标题
435 | 'select'=>[
436 | [
437 | 'name'=>'1小时',
438 | 'value'=>'hour'
439 | ],
440 | [
441 | 'name'=>'一天',
442 | 'value'=>'day'
443 |
444 | ],
445 | [
446 | 'name'=>'七天',
447 | 'value'=>'week'
448 | ],[
449 | 'name'=>'一月',
450 | 'value'=>'month'
451 | ],
452 | ]
453 |
454 | ],
455 | 'network'=>[ // 图表type
456 | 'title'=>'网络流量', // 标题
457 | 'select'=>[
458 | [
459 | 'name'=>'1小时',
460 | 'value'=>'hour'
461 | ],
462 | [
463 | 'name'=>'一天',
464 | 'value'=>'day'
465 |
466 | ],
467 | [
468 | 'name'=>'七天',
469 | 'value'=>'week'
470 | ],[
471 | 'name'=>'一月',
472 | 'value'=>'month'
473 | ],
474 | ]
475 |
476 | ],
477 | ];
478 |
479 | }
480 |
481 | function proxmoxlxc_ChartData($params){
482 |
483 | // 计算一下机器运行时间
484 |
485 | // 读取机器信息
486 | $uptime = proxmoxlxc_GET_lxc_info($params)['data']['uptime'];
487 | $timeframe = $params['chart']['select'];
488 | // if($uptime>=86400){
489 | // // 一天了
490 | // $timeframe = "day";
491 | // }else{
492 | // // 一小时
493 | // $timeframe = "hour";
494 | // }
495 | // return ['status'=>'error', 'msg'=>json_encode($uptime)];
496 |
497 | $info = json_decode(proxmoxlxc_request($params,"/api2/json/nodes/".$params['server_host']."/lxc/".$params['domain']."/rrddata?timeframe=".$timeframe),true);
498 |
499 |
500 |
501 | if($params['chart']['type'] == 'cpu'){
502 |
503 | // 组成数组
504 |
505 | foreach ($info['data'] as $value){
506 |
507 | $cpu = substr($value['cpu'] * 100,0,2);
508 | // $cpu = substr($value['cpu'],0,4);
509 | $result['data']['list'][0][] = [
510 | 'time' =>date('Y-m-d H:i:s', $value['time']),
511 | 'value' =>$cpu
512 | ];
513 |
514 | }
515 |
516 | $result["status"] = "success"; // 失败返回error
517 |
518 | $result["data"]["unit"] = "%"; //单位
519 |
520 | $result["data"]["chart_type"] = "line"; //支持 line线形,area区域填充
521 |
522 | $result["data"]["label"] = ["CPU使用率(%)"]; //对应每条线的label
523 |
524 |
525 | }elseif($params['chart']['type'] == 'mem'){
526 |
527 | // 组成数组
528 |
529 | foreach ($info['data'] as $value){
530 |
531 | if($value['maxmem']==""){
532 | break;
533 | }
534 | // $cpu = round(substr($value['cpu'],0,4) * 100,2);
535 | // $cpu = substr($value['cpu'],0,4);
536 | $max_mem = ($value['maxmem'] / 1024) / 1024 ;
537 | $run_mem = ($value['mem'] / 1024) / 1024 ;
538 |
539 | $mem = round($run_mem / $max_mem * 100,2);
540 | // (data.data.mem / 1024) / 1024 (run_mem/mem_max*100)
541 |
542 | // 内存百分比的
543 | $result['data']['list'][0][] = [
544 | 'time' =>date('Y-m-d H:i:s', $value['time']),
545 | 'value' =>$mem
546 | ];
547 |
548 |
549 | }
550 |
551 | $result["status"] = "success"; // 失败返回error
552 |
553 | $result["data"]["unit"] = "%"; //单位
554 |
555 | $result["data"]["chart_type"] = "line"; //支持 line线形,area区域填充
556 |
557 | $result["data"]["label"] = ["内存使用率(%)"]; //对应每条线的label
558 |
559 | }elseif($params['chart']['type'] == 'disk'){
560 |
561 | // 组成数组
562 | $dw = "K";
563 | foreach ($info['data'] as $value){
564 |
565 | $diskwrite = round($value['diskwrite'] / 1000,3);
566 | $diskread = round($value['diskread'] / 1000,3);
567 |
568 |
569 | // 计算IO
570 | $result['data']['list'][0][] = [
571 | 'time' =>date('Y-m-d H:i:s', $value['time']),
572 | 'value' =>$diskwrite
573 | ];
574 | $result['data']['list'][1][] = [
575 | 'time' =>date('Y-m-d H:i:s', $value['time']),
576 | 'value' =>$diskread
577 | ];
578 |
579 |
580 | }
581 |
582 | $result["status"] = "success"; // 失败返回error
583 |
584 | $result["data"]["unit"] = "K"; //单位
585 |
586 | $result["data"]["chart_type"] = "line"; //支持 line线形,area区域填充
587 |
588 | $result["data"]["label"] = ["写入速度(kb/s)","读取速度(kb/s)"]; //对应每条线的label
589 |
590 | }elseif($params['chart']['type'] == 'network'){
591 |
592 | // 组成数组
593 | foreach ($info['data'] as $value){
594 |
595 | $netin = round($value['netin'] / 1000,3);
596 | $netout = round($value['netout'] / 1000,3);
597 |
598 |
599 | // 计算IO
600 | $result['data']['list'][0][] = [
601 | 'time' =>date('Y-m-d H:i:s', $value['time']),
602 | 'value' =>$netin
603 | ];
604 | $result['data']['list'][1][] = [
605 | 'time' =>date('Y-m-d H:i:s', $value['time']),
606 | 'value' =>$netout
607 | ];
608 |
609 |
610 | }
611 |
612 | $result["status"] = "success"; // 失败返回error
613 |
614 | $result["data"]["unit"] = "K"; //单位
615 |
616 | $result["data"]["chart_type"] = "line"; //支持 line线形,area区域填充
617 |
618 | $result["data"]["label"] = ["入网流量(KB/s)","出网流量(KB/s)"]; //对应每条线的label
619 |
620 | }
621 |
622 | return $result;
623 |
624 | }
625 |
626 | // vnc
627 | function proxmoxlxc_Vnc($params){
628 |
629 | // 判断是否存在VNC控制台的后端程序
630 |
631 | if(!proxmoxlxc_vnc_if($params)){
632 |
633 | return ['status'=>'error','msg'=>'VNC功能未启用'];
634 |
635 | }
636 |
637 |
638 | $ticket = proxmoxlxc_get_ticket($params);
639 | if($ticket){
640 | //noVNC 登入
641 | //return ['status'=>'success','msg'=>'VNC连接创建成功','url'=>$params['server_http_prefix']."://".$params['server_ip'].":".$params['port']."/novnc/mgnovnc.html?novnc=1&console=lxc&node=".$params['server_host']."&vmid=".$params['domain']."&token=".$ticket];
642 | //XtermJS 登入
643 | return ['status'=>'success','msg'=>'VNC连接创建成功','url'=>$params['server_http_prefix']."://".$params['server_ip'].":".$params['port']."/novnc/mgnovnc.html?xtermjs=1&console=lxc&node=".$params['server_host']."&vmid=".$params['domain']."&token=".$ticket];
644 | //return ['status'=>'success','msg'=>'VNC连接创建成功','url'=>$params['server_http_prefix']."://".$params['server_ip'].":".$params['port']."?console=shell&xtermjs=1"."&vmid=".$params['domain']."&node=".$params['server_host']."&token=".$ticket];
645 | }
646 |
647 |
648 | // 不存在返回控制面板
649 |
650 |
651 |
652 |
653 |
654 | // return ['status'=>'success','msg'=>'打开面板成功','url'=>$params['server_http_prefix']."://".$params['server_ip'].":".$params['port']];
655 | }
656 |
657 |
658 |
659 | // 开通方法
660 | function proxmoxlxc_CreateAccount($params){
661 |
662 |
663 |
664 |
665 |
666 |
667 | $ip_while_num_max = 100; // IP生成循环次数,如果超过一定阈值直接弹出IP不足
668 | $ip_while_num = 0 ;
669 | $vmid = proxmoxlxc_nextid($params);
670 | /*构建IP地址 只能暂时分配/24的地址*/
671 |
672 | // 分割IP段
673 |
674 | // 先把段分割成起始跟结束
675 | $temp_ip_ = explode(",",$params['configoptions']['ip_pool']);
676 |
677 | $temp_ip_start = explode(".",$temp_ip_[0]);
678 |
679 | $temp_ip_end = explode(".",$temp_ip_[1]);
680 |
681 | // 开始分配IP地址
682 | while_ip:
683 |
684 | if($ip_while_num>=$ip_while_num_max ){
685 | return ['status'=>'error','msg'=>"无可用IP地址"];
686 | }
687 |
688 | $ip = $temp_ip_start[0].".".$temp_ip_start[1].".".$temp_ip_start[2].".".rand($temp_ip_start[3],$temp_ip_end[3]);
689 |
690 |
691 | // 验证IP是否占用
692 | $file = fopen(__DIR__."/ip_pool.json","r");
693 |
694 | $ip_json =json_decode(fread($file,filesize(__DIR__."/ip_pool.json")),true);
695 |
696 | fclose($file);
697 |
698 |
699 | foreach ($ip_json as $value){
700 | if($value['ip'] == $ip){
701 | // 存在相同的IP 冲突,重新获取IP
702 | $ip_while_num = $ip_while_num + 1;
703 | goto while_ip;
704 |
705 | }
706 | }
707 |
708 | // 临时写在这里
709 | // return ['status'=>'error','msg'=>proxmoxlxc_user_add($params,$ip,$params['password'],$vmid)];
710 |
711 | if(!proxmoxlxc_user_add($params,$ip,$params['password'],$vmid)){
712 |
713 | return ['status'=>'error','msg'=>"创建用户分配权限失败"];
714 |
715 | }
716 |
717 | // 构建JSON结构
718 |
719 | $json_network['server_vmid'] = $vmid;//对应主机VMID
720 | $json_network['bridge']=$params['configoptions']['net_name'];
721 | $json_network['ip'] = $ip;
722 | $json_network['mask'] = $params['configoptions']['Mask'];
723 | $json_network['gateway'] = $params['configoptions']['gateway'];
724 | $json_network['rate']=$params['configoptions_upgrade']['network'];
725 |
726 | $ip_json[$ip] = $json_network;
727 |
728 | // 写入库
729 | $file = fopen(__DIR__."/ip_pool.json","w");
730 |
731 | fwrite($file,json_encode($ip_json));
732 |
733 | fclose($file);
734 |
735 | // return ['status'=>'error','msg'=>json_encode($ip_json)];
736 |
737 | /*构建传递参数*/
738 |
739 | $network['name']='eth0';
740 | $network['bridge']=$params['configoptions']['net_name'];
741 | $network['gw']=$params['configoptions']['gateway'];
742 | $network['ip']=$ip."/".$params['configoptions']['Mask'];
743 | $network['rate']=$params['configoptions_upgrade']['network'];
744 | foreach ($network as $key=>$value){
745 | if($network_body==""){
746 | $network_body = $key."%3D".$value;
747 | }else{
748 | $network_body = $network_body."%2C".$key."%3D".$value;
749 | }
750 | }
751 | $data['start']=1;
752 | $data['ostemplate'] = $params['configoptions_upgrade']['os']; // 模板
753 | $data['vmid'] = $vmid; // vmid
754 | $data['hostname']=$params['domain'];
755 | $data['unprivileged']=1;
756 | if($params['configoptions']['nesting'] == 'open'){ //嵌套虚拟化允许
757 | // $data['features']='nesting'."%3D"."1"."%2C".'keyctl'."%3D"."1";
758 | $data['features']='nesting'."%3D"."1";
759 | // $data['features']='nesting'."%3D"."1"."%2C".'keyctl'."%3D"."1"."%2C".'fuse'."%3D"."1";
760 | }
761 | $data['password']=$params['password'];
762 | $data['rootfs']=$params['configoptions']['system_disk'].":".$params['configoptions_upgrade']['disk'];
763 | $data['cores']=$params['configoptions_upgrade']['cpu'];
764 | $data['memory']=$params['configoptions_upgrade']['memory'];
765 | $data['net0']=$network_body;
766 | $data['cmode']='console';
767 | $data['onboot'] = true;
768 | $data['nameserver'] = $params['configoptions']['dns'];
769 | $data['description'] = "
ArcCloud
来自: 智简魔方ProxmoxVE-LXC模块开通用户:".$params['user_info']['username']."|".$params['user_info']['id']."产品编号:".$params['hostid']."产品密码:".$params['password'];//描述
770 | // $data['bwlimit']=$params['configoptions_upgrade']['network'];
771 | if($params['configoptions']['swap'] == '1:1'){
772 | // 对等分配swap
773 | $data['swap'] = $params['configoptions_upgrade']['memory'];
774 | }elseif($params['configoptions']['swap'] == '1024'){
775 | // 分配1G
776 | $data['swap'] = '1024';
777 | }
778 |
779 | /*请求后端创建服务器*/
780 | $info = json_decode(proxmoxlxc_request($params,"/api2/extjs/nodes/".$params['server_host']."/lxc",$data,"POST"),true);
781 | /*结果处理*/
782 |
783 |
784 | if($info['success']){
785 | // 判断是否为nat机器 如果是就执行下默认映射操作
786 | if($params['configoptions']['nat']=="nat"){
787 | $post['comment'] = $params['domain']."的远程端口";
788 | $post['type']='tcp+udp';
789 | $post['lan_port'] = "22";
790 | $post['lan_addr'] = $ip;
791 |
792 |
793 |
794 | $port_info = proxmoxlxc_nat_add($params,$post);
795 |
796 | if($port_info['ErrMsg']!="Success"){
797 | active_logs("创建端口映射时出现错误:".json_encode($port_info),$params['uid'],2);
798 | return ['status'=>'error','msg'=>json_encode($port_info)];
799 | }
800 | $update['port'] =$port_info['wan_port'];
801 | }
802 |
803 |
804 |
805 | /*更新数据列表*/
806 |
807 |
808 |
809 | $update['dedicatedip'] =$ip;
810 | $update['domain']=$data['vmid'];
811 | Db::name('host')->where('id', $params['hostid'])->update($update);
812 |
813 |
814 |
815 |
816 | /*写入本地文件*/
817 |
818 | return ['status'=>'success'];
819 | }else{
820 | // 错误处理 10086
821 | return ['status'=>'error','msg'=>json_encode($info).json_encode($params).json_encode($data['ostemplate'])];
822 | }
823 |
824 |
825 |
826 |
827 | }
828 |
829 | /*管理*/
830 |
831 | // 删除机器
832 | function proxmoxlxc_TerminateAccount ($params){
833 |
834 |
835 | if(!proxmoxlxc_user_del($params)){
836 | // return ['status'=>'error','msg'=>'用户删除失败'];
837 | active_logs($params['dedicatedip']."@pve用户删除失败",$params['uid'],2);
838 | }
839 |
840 |
841 | $info = json_decode(proxmoxlxc_request($params,"/api2/json/nodes/".$params['server_host']."/lxc/".$params['domain']."?purge=1&destroy-unreferenced-disks=1&force=1","","DELETE"),true);
842 | if($info['data']==null || $info == null){
843 |
844 |
845 |
846 |
847 |
848 | return ['status'=>'error','msg'=>"/api2/json/nodes/".$params['server_host']."/lxc/".$params['domain']."purge=1&destroy-unreferenced-disks=1&force=1"];
849 | }else{
850 | /*把操作保存到ip池中 释放IP*/
851 |
852 | $file = fopen(__DIR__."/ip_pool.json","r");
853 |
854 | $ip_json =json_decode(fread($file,filesize(__DIR__."/ip_pool.json")),true);
855 |
856 | fclose($file);
857 |
858 |
859 |
860 | // 删除IP
861 | unset($ip_json[$params['dedicatedip']]);
862 |
863 | // 写入库
864 | $file = fopen(__DIR__."/ip_pool.json","w");
865 |
866 | fwrite($file,json_encode($ip_json));
867 |
868 | fclose($file);
869 |
870 | // 判断是否为映射机
871 |
872 | // 判断是否为nat机器 如果是就执行下默认映射操作
873 | if($params['configoptions']['nat']=="nat"){
874 |
875 | // 操作删除映射
876 | $nat_list = proxmoxlxc_nat_get_list($params);
877 |
878 | if($nat_list['ErrMsg']=='Success'){
879 | // 获取成功
880 |
881 | foreach($nat_list['Data']['data'] as $key=>$value){
882 | // 执行删除
883 | $port['id'] = $value['id'];
884 | $port['wan_port'] = $value['wan_port'];
885 | $return_info = proxmoxlxc_nat_del($params,$port);
886 |
887 | // active_logs("执行删除映射结果:".json_encode($return_info),$params['uid'],2);
888 |
889 | }
890 |
891 |
892 | }else{
893 | // 获取失败,写入日志
894 | active_logs("执行删除映射失败:".json_encode($nat_list),$params['uid'],2);
895 | }
896 |
897 |
898 | }
899 |
900 |
901 | // 便利映射数量
902 |
903 | // 删除映射
904 |
905 |
906 | return ['status'=>'success'];
907 | }
908 |
909 |
910 | }
911 |
912 | /*电源管理*/
913 |
914 | // 开机
915 | function proxmoxlxc_On($params){
916 | $info = json_decode(proxmoxlxc_request($params,"/api2/json/nodes/".$params['server_host']."/lxc/".$params['domain']."/status/start",$data,"POST"),true);
917 | if($info['data']==null || $info == null){
918 | return ['status'=>'error','msg'=>'操作失败'];
919 | }else{
920 | return ['status'=>'success'];
921 | }
922 |
923 |
924 |
925 | }
926 | // 关机
927 | function proxmoxlxc_Off($params){
928 | $info = json_decode(proxmoxlxc_request($params,"/api2/json/nodes/".$params['server_host']."/lxc/".$params['domain']."/status/shutdown",$data,"POST"),true);
929 | if($info['data']==null || $info == null){
930 | return ['status'=>'error','msg'=>'操作失败'];
931 | }else{
932 | return ['status'=>'success'];
933 | }
934 |
935 |
936 | }
937 |
938 | // 重启
939 | function proxmoxlxc_Reboot($params){
940 | $info = json_decode(proxmoxlxc_request($params,"/api2/json/nodes/".$params['server_host']."/lxc/".$params['domain']."/status/reboot",$data,"POST"),true);
941 | if($info['data']==null || $info == null){
942 | return ['status'=>'error','msg'=>'操作失败'];
943 | }else{
944 | return ['status'=>'success'];
945 | }
946 | }
947 |
948 | // 强制关机
949 | function proxmoxlxc_HardOff ($params){
950 | $info = json_decode(proxmoxlxc_request($params,"/api2/json/nodes/".$params['server_host']."/lxc/".$params['domain']."/status/stop",$data,"POST"),true);
951 | if($info['data']==null || $info == null){
952 | return ['status'=>'error','msg'=>'操作失败'];
953 | }else{
954 | return ['status'=>'success'];
955 | }
956 | }
957 |
958 |
959 | // 暂停
960 | function proxmoxlxc_SuspendAccount ($params){
961 | proxmoxlxc_HardOff($params);
962 | }
963 | // 解除暂停
964 | function proxmoxlxc_UnsuspendAccount($params){
965 |
966 | }
967 |
968 |
969 | /*功能函数*/
970 |
971 | function proxmoxlxc_Getcurrent($params){
972 | $post = input('post.');
973 |
974 |
975 | // 验证PVE状态
976 | if(!proxmoxlxc_Pvestatus($params)){
977 | return ['status'=>'error','msg'=>'受控端异常'];
978 | }
979 |
980 |
981 | return proxmoxlxc_GET_lxc_info($params);
982 | }
983 |
984 |
985 | // 获取VMID
986 | function proxmoxlxc_nextid($params) {
987 | $vmid_file_path = __DIR__ . "/vmid.json";
988 | $product_unique_value = $params['configoptions']['product_unique_value'];
989 | $start_vmid = (int)$params['configoptions']['vmid_start'];
990 |
991 | // 初始化 $vmid_data
992 | $vmid_data = [];
993 |
994 | // 文件存在并且可读
995 | if (file_exists($vmid_file_path)) {
996 | $vmid_file = fopen($vmid_file_path, "r+");
997 | if (flock($vmid_file, LOCK_EX)) {
998 | $file_content = fread($vmid_file, filesize($vmid_file_path));
999 | if ($file_content !== false && trim($file_content) !== '') {
1000 | $vmid_data = json_decode($file_content, true);
1001 |
1002 | // 检查是否JSON解析失败
1003 | if (json_last_error() !== JSON_ERROR_NONE) {
1004 | $vmid_data = []; // 解析失败,初始化为空数组
1005 | }
1006 | }
1007 |
1008 | if (isset($vmid_data[$product_unique_value])) {
1009 | // 获取当前 VMID 并返回
1010 | $current_vmid = (int)$vmid_data[$product_unique_value]['data'];
1011 |
1012 | // 更新 VMID 并写回文件
1013 | $vmid_data[$product_unique_value]['data'] = $current_vmid + 1;
1014 | ftruncate($vmid_file, 0);
1015 | rewind($vmid_file);
1016 | fwrite($vmid_file, json_encode($vmid_data, JSON_PRETTY_PRINT));
1017 | fflush($vmid_file);
1018 | flock($vmid_file, LOCK_UN);
1019 | fclose($vmid_file);
1020 |
1021 | return $current_vmid;
1022 | }
1023 |
1024 | flock($vmid_file, LOCK_UN);
1025 | }
1026 | fclose($vmid_file);
1027 | }
1028 |
1029 | // 文件不存在或没有产品唯一值,初始化并写入
1030 | $vmid_data[$product_unique_value] = ['data' => $start_vmid];
1031 |
1032 | // 合并数据
1033 | if (file_exists($vmid_file_path)) {
1034 | $existing_data = json_decode(file_get_contents($vmid_file_path), true);
1035 | if (json_last_error() === JSON_ERROR_NONE && is_array($existing_data)) {
1036 | $vmid_data = array_merge($existing_data, $vmid_data);
1037 | }
1038 | }
1039 |
1040 | $vmid_file = fopen($vmid_file_path, "w");
1041 | if (flock($vmid_file, LOCK_EX)) {
1042 | fwrite($vmid_file, json_encode($vmid_data, JSON_PRETTY_PRINT));
1043 | fflush($vmid_file);
1044 | flock($vmid_file, LOCK_UN);
1045 | }
1046 | fclose($vmid_file);
1047 |
1048 | return $start_vmid;
1049 | }
1050 |
1051 |
1052 |
1053 | // 删除VMID
1054 | // 这个功能先咕咕,没思路
1055 | function proxmoxlxc_delete_id($params) {
1056 |
1057 | }
1058 |
1059 |
1060 | // 没有唯一id,被取代
1061 | // 获取VMID
1062 | // function proxmoxlxc_nextid($params) {
1063 | // $vmid_file_path = __DIR__ . "/vmid.json";
1064 |
1065 | // // 检查文件是否存在
1066 | // if (file_exists($vmid_file_path)) {
1067 | // $vmid_file_size = filesize($vmid_file_path);
1068 |
1069 | // if ($vmid_file_size > 0) {
1070 | // // 文件存在且不为空,读取 JSON 数据
1071 | // $vmid_file = fopen($vmid_file_path, "r");
1072 | // $vmid_data = json_decode(fread($vmid_file, $vmid_file_size), true);
1073 | // fclose($vmid_file);
1074 |
1075 | // // 获取当前 VMID 并返回
1076 | // $current_vmid = (int)$vmid_data['data'];
1077 |
1078 | // // 将新的 VMID 加 1 并写回文件
1079 | // $new_vmid_data = ['data' => $current_vmid + 1];
1080 | // $vmid_file = fopen($vmid_file_path, "w");
1081 | // fwrite($vmid_file, json_encode($new_vmid_data, JSON_PRETTY_PRINT));
1082 | // fclose($vmid_file);
1083 |
1084 | // return $current_vmid;
1085 | // }
1086 | // }
1087 |
1088 | // // 文件不存在或为空,写入初始 VMID 并返回
1089 | // $start_vmid = (int)$params['configoptions']['vmid_start'];
1090 | // $new_vmid_data = ['data' => $start_vmid];
1091 | // $vmid_file = fopen($vmid_file_path, "w");
1092 | // fwrite($vmid_file, json_encode($new_vmid_data, JSON_PRETTY_PRINT));
1093 | // fclose($vmid_file);
1094 |
1095 | // return $start_vmid;
1096 | // }
1097 |
1098 |
1099 |
1100 | //老的版本
1101 | // 获取VMID
1102 | // function proxmoxlxc_nextid($params){
1103 |
1104 | // if(!proxmoxlxc_Pvestatus($params)){
1105 | // // return ['status'=>'error','msg'=>'受控端异常'];
1106 | // return 0;
1107 | // }else{
1108 | // $request = json_decode(proxmoxlxc_request($params,"/api2/json/cluster/nextid"),true);
1109 | // return $request['data'];
1110 | // }
1111 | // }
1112 |
1113 |
1114 |
1115 | // 检测pve状态
1116 | function proxmoxlxc_Pvestatus($params){
1117 |
1118 | return 1;
1119 |
1120 | }
1121 |
1122 | function proxmoxlxc_status($params){
1123 |
1124 |
1125 | $request = json_decode(proxmoxlxc_request($params,"/api2/json/nodes/".$params['server_host']."/lxc/".$params['domain']."/status/current"),true);
1126 | $result['status'] = 'success';
1127 | if($request['data']['status']=='running'){
1128 | // 运行中
1129 | $result['data']['status'] = 'on';
1130 | $result['data']['des'] = '运行中';
1131 | }elseif($request['data']['status']=='stopped'){
1132 | $result['data']['status'] = 'off';
1133 | $result['data']['des'] = '关机';
1134 | }else{
1135 | $result['data']['status'] = 'unknown';
1136 | $result['data']['des'] = '未知';
1137 | }
1138 |
1139 | return $result;
1140 |
1141 | }
1142 |
1143 | // 获取LXC信息
1144 | function proxmoxlxc_GET_lxc_info($params){
1145 | $request = json_decode(proxmoxlxc_request($params,"/api2/json/nodes/".$params['server_host']."/lxc/".$params['domain']."/status/current"),true);
1146 | return $request;
1147 |
1148 | }
1149 |
1150 | // 获取LXC配置信息
1151 | function proxmoxlxc_GET_lxc_config($params){
1152 | $request = json_decode(proxmoxlxc_request($params,"/api2/json/nodes/".$params['server_host']."/lxc/".$params['domain']."/config"),true);
1153 |
1154 | if($request['data']==null||$request==null){
1155 | return false;
1156 | }
1157 |
1158 | // 把网卡配置格式化一下
1159 | $temp_net_info = explode(",",$request['data']['net0']);
1160 | foreach ($temp_net_info as $value){
1161 |
1162 | $temp = explode("=",$value);
1163 |
1164 | $temp_ini[$temp[0]]=$temp[1];
1165 |
1166 |
1167 | }
1168 | $request['data']['net0'] = $temp_ini;
1169 |
1170 | // 格式化硬盘
1171 | $temp_disk_info = explode(",",$request['data']['rootfs']);
1172 |
1173 | $request['data']['rootfs'] = explode(",",$request['data']['rootfs']);;
1174 |
1175 |
1176 | return $request;
1177 |
1178 | }
1179 |
1180 | /*快照相关*/
1181 |
1182 | // 获取快照列表
1183 | function proxmoxlxc_GET_lxc_snapshot_list($params){
1184 | $request = json_decode(proxmoxlxc_request($params,"/api2/json/nodes/".$params['server_host']."/lxc/".$params['domain']."/snapshot/"),true);
1185 | $temp = [];
1186 | foreach ($request['data'] as $value){
1187 |
1188 | if($value['name']=="current"){
1189 | continue;
1190 | }
1191 | $value['snaptime'] = date('Y-m-d H:i:s',$value['snaptime']);
1192 |
1193 | $value['description'] = mb_convert_encoding($value['description'], 'GBK','UTF-8');
1194 | // $a = 1;
1195 | array_push($temp,$value);
1196 |
1197 | // $temp['name'] = $value['name'];
1198 | }
1199 |
1200 | return $temp;
1201 |
1202 | }
1203 |
1204 | // 删除快照
1205 | function proxmoxlxc_delete_snapshot($params){
1206 |
1207 | $post = input('post.');
1208 |
1209 | $request = json_decode(proxmoxlxc_request($params,"/api2/json/nodes/".$params['server_host']."/lxc/".$params['domain']."/snapshot/".$post['name'],"","DELETE"),true);
1210 |
1211 | if($request['data']==null||$request==null){
1212 | return false;
1213 | }
1214 |
1215 | return ['status'=>'200','msg'=>'快照删除成功'];
1216 |
1217 | }
1218 |
1219 | // 回滚
1220 | function proxmoxlxc_RollBACK_snapshot($params){
1221 |
1222 | $post = input('post.');
1223 |
1224 | $request = json_decode(proxmoxlxc_request($params,"/api2/json/nodes/".$params['server_host']."/lxc/".$params['domain']."/snapshot/".$post['name']."/rollback","","POST"),true);
1225 |
1226 | if($request['data']==null||$request==null){
1227 | return false;
1228 | }
1229 |
1230 | return ['status'=>'200','msg'=>'回滚成功'];
1231 |
1232 | }
1233 |
1234 | // 创建快照
1235 | function proxmoxlxc_create_snapshot($params){
1236 |
1237 | $post = input('post.');
1238 |
1239 | // 检查传入的 快照名称是否符合后端pve服务器的要求
1240 | if (!isset($post['name']) || !preg_match('/^[a-zA-Z][a-zA-Z0-9_]{1,}$/', $post['name'])) {
1241 | return ['ErrMsg' => 'IllegalFormat'];
1242 | }
1243 |
1244 | // 检查快照描述是否超出了100字
1245 | if (!isset($post['description']) || strlen($post['description']) > 100) {
1246 | return ['ErrMsg' => 'IllegalFormat'];
1247 | }
1248 |
1249 | $data['snapname'] = $post['name'];
1250 | $data['description'] = $post['description'];
1251 |
1252 | $request = json_decode(proxmoxlxc_request($params,"/api2/json/nodes/".$params['server_host']."/lxc/".$params['domain']."/snapshot/",$data,"POST"),true);
1253 |
1254 | // 调试代码
1255 | //active_logs(json_encode($request).json_encode($data).$params['server_host'].$params['domain']);
1256 |
1257 | if($request['data']==null||$request==null){
1258 | return false;
1259 | }
1260 |
1261 | return ['status'=>'200','msg'=>'创建成功'];
1262 |
1263 | }
1264 |
1265 | /*任务相关*/
1266 |
1267 | // 获取任务列表
1268 | function proxmoxlxc_tasks_get_list($params){
1269 |
1270 |
1271 | $request = json_decode(proxmoxlxc_request($params,"/api2/json/nodes/".$params['server_host']."/tasks?vmid=".$params['domain']."&start=0&limit=10"),true);
1272 |
1273 | if($request['data']==null||$request==null){
1274 | return false;
1275 | }
1276 |
1277 |
1278 | $temp = [];
1279 | foreach ($request['data'] as $value){
1280 |
1281 | $value['starttime'] = date('Y-m-d H:i:s',$value['starttime']);
1282 | $value['endtime'] = date('Y-m-d H:i:s',$value['endtime']);
1283 |
1284 | // $a = 1;
1285 | array_push($temp,$value);
1286 |
1287 | // $temp['name'] = $value['name'];
1288 | }
1289 |
1290 | return $temp;
1291 |
1292 |
1293 |
1294 | // return $request['data'];
1295 |
1296 |
1297 | }
1298 |
1299 |
1300 | /*
1301 | 端口映射相关操作
1302 | */
1303 |
1304 |
1305 |
1306 | // 获取列表
1307 | function proxmoxlxc_nat_get_list($params){
1308 | $url = $params['configoptions']['ikuai_url']."/Action/call";
1309 |
1310 |
1311 |
1312 | $data['func_name'] = "dnat";
1313 | $data['action'] = "show";
1314 | $data['param'] = [
1315 | "TYPE"=>"total,data",
1316 | "limit"=>"0,20",
1317 | "FINDS"=>"lan_addr,lan_port,wan_port,comment",
1318 | "KEYWORDS"=> $params['dedicatedip']
1319 |
1320 | ];
1321 |
1322 | $info = json_decode(proxmoxlxc_nat_request($params,$url,$data),true);
1323 |
1324 | // echo json_encode($data);
1325 |
1326 | return $info;
1327 |
1328 | // return proxmoxlxc_nat_request($params,$url,$dataa);
1329 | }
1330 |
1331 |
1332 |
1333 | // 添加映射
1334 | function proxmoxlxc_nat_add($params, $post = "") {
1335 | // 判断是从哪里请求的
1336 | if ($post == "") {
1337 | // 从前台请求的 需要手动拉一下内网ip
1338 | $post = input('post.');
1339 | $post['lan_addr'] = $params['dedicatedip'];
1340 | }
1341 |
1342 | // 映射数量限制
1343 | $nat_list = proxmoxlxc_nat_get_list($params);
1344 | $nat_limit_error = ['ErrMsg' => 'ReachLimit'];
1345 |
1346 | if ($nat_list['Data']['total'] == $params['configoptions_upgrade']['nat_limit']) {
1347 | return $nat_limit_error;
1348 | }
1349 |
1350 | // 获取端口范围
1351 | // $port_pool = explode(',', $params['configoptions']['port_pool_opt']);
1352 | // $min_port = (int)$port_pool[0];
1353 | // $max_port = (int)$port_pool[1];
1354 | $min_port = (int)$params['configoptions']['port_pool_start'];
1355 | $max_port = (int)$params['configoptions']['port_pool_end'];
1356 |
1357 | // 检查 post['wan_port'] 是否在合法范围内
1358 | if (!empty($post['wan_port']) && ($post['wan_port'] < $min_port || $post['wan_port'] > $max_port)) {
1359 | return ['ErrMsg' => 'IllegalPort'];
1360 | }
1361 |
1362 | // 端口分配代码开始
1363 | $port_while_num_max = 100; // 端口生成循环次数,如果超出阈值直接弹出端口不足
1364 | $port_while_num = 0;
1365 |
1366 | $port_pool_file_path = __DIR__ . "/port_pool.json";
1367 |
1368 | while (true) {
1369 | if ($port_while_num >= $port_while_num_max) {
1370 | return ['ErrMsg' => 'PortUnavailable'];
1371 | }
1372 |
1373 | // 如果未传递外网端口,就按照端口池随机生成
1374 | if (empty($post['wan_port'])) {
1375 | $port = rand($min_port, $max_port);
1376 | } else {
1377 | $port = $post['wan_port'];
1378 | }
1379 |
1380 | // 读取端口池文件
1381 | if (file_exists($port_pool_file_path)) {
1382 | $port_pool_file = fopen($port_pool_file_path, "r");
1383 | $port_pool_file_json = json_decode(fread($port_pool_file, filesize($port_pool_file_path)), true);
1384 | fclose($port_pool_file);
1385 | } else {
1386 | $port_pool_file_json = [];
1387 | }
1388 |
1389 | $port_taken = false;
1390 |
1391 | foreach ($port_pool_file_json as $key => $value) {
1392 | if ($key == $port) {
1393 | $port_taken = true;
1394 | break;
1395 | }
1396 | }
1397 |
1398 | if (!$port_taken) {
1399 | // 端口没有被占用,执行后续操作
1400 | //if (empty($post['wan_port'])) {
1401 | // 更新端口池文件
1402 | //$port_pool_file_json[$port] = ['server_vmid' => $params['domain']];
1403 | $port_pool_file_json[$port] = [
1404 | 'server_vmid' => $params['domain'],
1405 | 'server_ip' => $params['dedicatedip']
1406 | ];
1407 |
1408 | $port_pool_file = fopen($port_pool_file_path, "w");
1409 | fwrite($port_pool_file, json_encode($port_pool_file_json, JSON_PRETTY_PRINT));
1410 | fclose($port_pool_file);
1411 | //}
1412 |
1413 | $url = $params['configoptions']['ikuai_url'] . "/Action/call";
1414 | $data['func_name'] = "dnat";
1415 | $data['action'] = "add";
1416 | $data['param'] = [
1417 | "enabled" => "yes",
1418 | "comment" => $post['comment'],
1419 | "interface" => "all",
1420 | "lan_addr" => $post['lan_addr'],
1421 | "protocol" => $post['type'],
1422 | "wan_port" => $port,
1423 | "lan_port" => $post['lan_port'],
1424 | "src_addr" => ""
1425 | ];
1426 |
1427 | $info = json_decode(proxmoxlxc_nat_request($params, $url, $data), true);
1428 | $info['wan_port'] = $port;
1429 |
1430 | // 写入后台日志
1431 | //active_logs("执行创建映射函数返回结果: " . json_encode($info) . proxmoxlxc_nextid($params) . " | 端口池:" . json_encode($port_pool), $params['uid'], 2);
1432 | active_logs("执行创建映射函数返回结果: " . json_encode($info) . " | 端口池:" . json_encode($port_pool), $params['uid'], 2);
1433 | return $info;
1434 | } elseif (!empty($post['wan_port'])) {
1435 | // 如果 post['wan_port'] 有值且被占用
1436 | return ['ErrMsg' => 'PortCantBeUse'];
1437 | }
1438 |
1439 | // 增加尝试次数
1440 | $port_while_num++;
1441 | }
1442 | }
1443 |
1444 |
1445 | // 之前的,作为备份,不支持外部端口自定义
1446 | // 添加映射
1447 | // function proxmoxlxc_nat_add($params,$post=""){
1448 |
1449 | // // 判断是从哪里请求的
1450 | // if($post==""){
1451 | // // 从前台请求的 需要手动拉一下内网ip
1452 | // $post = input('post.');
1453 | // $post['lan_addr'] = $params['dedicatedip'];
1454 | // }
1455 |
1456 | // // 映射数量限制
1457 | // $nat_list = proxmoxlxc_nat_get_list($params);
1458 |
1459 | // $nat_limit_error = ['ErrMsg' => 'ReachLimit'];
1460 |
1461 | // if ($nat_list['Data']['total'] == $params['configoptions_upgrade']['nat_limit']) {
1462 | // return $nat_limit_error;
1463 | // }
1464 |
1465 | // // 获取端口范围
1466 | // $port_pool = explode(',', $params['configoptions']['port_pool']);
1467 | // $min_port = (int)$port_pool[0];
1468 | // $max_port = (int)$port_pool[1];
1469 |
1470 | // // 如果未传递外网端口,就按照端口池随机生成
1471 | // if($post['wan_port']==""){
1472 | // //$post['wan_port'] = rand(20000,60000);
1473 | // $post['wan_port'] = rand($min_port,$max_port);
1474 | // }
1475 |
1476 | // $url = $params['configoptions']['ikuai_url']."/Action/call";
1477 |
1478 |
1479 |
1480 | // $data['func_name'] = "dnat";
1481 | // $data['action'] = "add";
1482 | // $data['param'] = [
1483 | // "enabled"=>"yes",
1484 | // "comment"=>$post['comment'],
1485 | // "interface"=>"all",
1486 | // "lan_addr"=> $post['lan_addr'],
1487 | // "protocol"=>$post['type'],
1488 | // "wan_port"=>$post['wan_port'],
1489 | // "lan_port"=>$post['lan_port'],
1490 | // "src_addr"=>""
1491 |
1492 | // ];
1493 |
1494 | // $info = json_decode(proxmoxlxc_nat_request($params,$url,$data),true);
1495 | // $info['wan_port'] = $post['wan_port'];
1496 | // // echo json_encode($data);
1497 |
1498 | // // 写入后台日志
1499 | // active_logs("执行创建映射函数返回结果:".json_encode($info),$params['uid'],2);
1500 | // return $info;
1501 |
1502 | // }
1503 |
1504 |
1505 |
1506 | // 删除映射
1507 | function proxmoxlxc_nat_del($params, $post = "") {
1508 | // 判断是从哪里请求的
1509 | if ($post == "") {
1510 | // 从前台请求的
1511 | $post = input('post.');
1512 | }
1513 |
1514 | // 构建 API 请求 URL 和数据
1515 | $url = $params['configoptions']['ikuai_url'] . "/Action/call";
1516 | $data['func_name'] = "dnat";
1517 | $data['action'] = "del";
1518 | $data['param'] = [
1519 | "id" => $post['id'],
1520 | ];
1521 |
1522 | // 发送请求并获取响应
1523 | $info = json_decode(proxmoxlxc_nat_request($params, $url, $data), true);
1524 |
1525 | // 如果删除成功,处理 port_pool.json 文件
1526 | if ($info['ErrMsg'] == 'Success') {
1527 | $port_pool_file_path = __DIR__ . "/port_pool.json";
1528 |
1529 | if (file_exists($port_pool_file_path)) {
1530 | // 读取端口池文件
1531 | $port_pool_file_content = file_get_contents($port_pool_file_path);
1532 | $port_pool_file_json = json_decode($port_pool_file_content, true);
1533 |
1534 | // 根据请求中的端口号删除相应记录
1535 | $wan_port = $post['wan_port'] ?? null;
1536 | if ($wan_port && array_key_exists($wan_port, $port_pool_file_json)) {
1537 | unset($port_pool_file_json[$wan_port]);
1538 |
1539 | // 保存更新后的端口池文件
1540 | $port_pool_file = fopen($port_pool_file_path, "w");
1541 | if (flock($port_pool_file, LOCK_EX)) { // 加锁,避免并发问题
1542 | fwrite($port_pool_file, json_encode($port_pool_file_json, JSON_PRETTY_PRINT));
1543 | fflush($port_pool_file); // 刷新缓冲区
1544 | flock($port_pool_file, LOCK_UN); // 解锁
1545 | }
1546 | fclose($port_pool_file);
1547 | } else {
1548 | // 添加调试日志:未找到要删除的端口
1549 | active_logs("端口 {$wan_port} 不在端口池中或未指定.", $params['uid'], 2);
1550 | }
1551 | } else {
1552 | // 添加调试日志:文件不存在
1553 | active_logs("端口池文件不存在: " . $port_pool_file_path, $params['uid'], 2);
1554 | }
1555 | } else {
1556 | // 添加调试日志:API 删除失败
1557 | active_logs("删除映射 API 返回失败: " . json_encode($info), $params['uid'], 2);
1558 | }
1559 |
1560 | // 写入后台日志
1561 | active_logs("执行删除映射函数返回结果:" . json_encode($info), $params['uid'], 2);
1562 | return $info;
1563 | }
1564 |
1565 |
1566 |
1567 |
1568 |
1569 |
1570 | // 过时的,不能删除port_pool.json文件中的映射记录
1571 | // 删除映射
1572 | // function proxmoxlxc_nat_del($params,$post=""){
1573 |
1574 |
1575 | // // 判断是从哪里请求的
1576 | // if($post==""){
1577 | // // 从前台请求的
1578 | // $post = input('post.');
1579 | // }
1580 |
1581 |
1582 |
1583 | // $url = $params['configoptions']['ikuai_url']."/Action/call";
1584 |
1585 |
1586 |
1587 | // $data['func_name'] = "dnat";
1588 | // $data['action'] = "del";
1589 | // $data['param'] = [
1590 | // "id"=>$post['id'],
1591 |
1592 |
1593 | // ];
1594 |
1595 | // $info = json_decode(proxmoxlxc_nat_request($params,$url,$data),true);
1596 |
1597 | // // echo json_encode($data);
1598 |
1599 | // // 写入后台日志
1600 | // active_logs("执行删除映射函数返回结果:".json_encode($info),$params['uid'],2);
1601 | // return $info;
1602 |
1603 | // }
1604 |
1605 | // 请求方法
1606 | function proxmoxlxc_nat_request($params,$url,$data){
1607 |
1608 | /*登陆获取cookie*/
1609 | // 定义cookie存放路径
1610 | $cookie_file = dirname(__FILE__).'/cookie/ikuai_'.$params['server_ip'].'.cookie';
1611 | $login_url = $params['configoptions']['ikuai_url']."/Action/login";
1612 | // $post_string = "{\"username\":\"admin\",\"passwd\":\"4e62f5790eea7611dd3c3e0ebc6d9c18\",\"pass\":\"c2FsdF8xMXhpYW93ZWlDMTIz\",\"remember_password\":\"true\"}";
1613 |
1614 | $post_data['username'] = $params['configoptions']['ikuai_username'];
1615 |
1616 | $post_data['passwd']= md5($params['configoptions']['ikuai_password']);
1617 |
1618 | $post_data['pass'] = md5("salt_11".$params['configoptions']['ikuai_password']);
1619 |
1620 | $post_data['remember_password'] = true;
1621 |
1622 |
1623 |
1624 |
1625 | $ch = curl_init($login_url); //初始化
1626 | curl_setopt($ch, CURLOPT_HEADER, 0); //不返回header部分
1627 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); //返回字符串,??直接输出
1628 | curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie_file); //存储cookies
1629 | curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($post_data));
1630 | curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE); // 屏蔽SSL
1631 | curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE); // 屏蔽SSL
1632 | curl_exec($ch);
1633 | curl_close($ch);
1634 | // echo json_encode($post_data);
1635 |
1636 |
1637 | // 使用cookie请求
1638 |
1639 | $cookie_file = dirname(__FILE__).'/cookie/ikuai_'.$params['server_ip'].'.cookie';
1640 |
1641 | // $url =$params['configoptions']['ikuai_url']."/Action/call";
1642 | // $post_string = "{\"func_name\":\"dnat\",\"action\":\"show\",\"param\":{\"TYPE\":\"total,data\",\"limit\":\"0,20\",\"ORDER_BY\":\"\",\"ORDER\":\"\",\"FINDS\":\"lan_addr,lan_port,wan_port,comment\",\"KEYWORDS\":\"172.16.0.2\",\"FILTER1\":\"\",\"FILTER2\":\"\",\"FILTER3\":\"\",\"FILTER4\":\"\",\"FILTER5\":\"\"}}";
1643 |
1644 |
1645 | // echo "get:".json_encode($dataa);
1646 |
1647 | $ch = curl_init($url);
1648 | curl_setopt($ch, CURLOPT_HEADER, 0);
1649 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
1650 | curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_file); //使?上?获取的cookies
1651 | curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
1652 | curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE); // 屏蔽SSL
1653 | curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE); // 屏蔽SSL
1654 | $response = curl_exec($ch);
1655 | curl_close($ch);
1656 | return $response;
1657 |
1658 |
1659 |
1660 | }
1661 |
1662 |
1663 | /*用户管理相关方法*/
1664 |
1665 | // 创建用户
1666 | function proxmoxlxc_user_add($params,$username,$password,$vmid){
1667 | // return FALSE;
1668 |
1669 | // 构建请求参数
1670 | $data['userid'] = $username."@pve";
1671 | $data['password'] = $password;
1672 | $data['comment'] = "用户:".$params['user_info']['username']."|".$params['user_info']['id']."的VNC账号,服务器编号:".$vmid;
1673 |
1674 | $request = json_decode(proxmoxlxc_request($params,"/api2/extjs/access/users",$data,"POST"),true);
1675 |
1676 | if($request==null){
1677 | return FALSE;
1678 | }
1679 |
1680 | if($request['success']){
1681 | // 注册成功 开始分配权限
1682 |
1683 | // 构建参数
1684 | $qx['path'] = "%2Fvms%2F".$vmid;
1685 | $qx['users']= $data['userid'];
1686 | $qx['roles'] = "PVEVMUser";
1687 |
1688 | // 请求
1689 | $request1 = json_decode(proxmoxlxc_request($params,"/api2/extjs/access/acl",$qx,"PUT"),true);
1690 |
1691 | return $request1;
1692 |
1693 | }
1694 |
1695 | // return "游结果咩:".json_encode($request);
1696 | }
1697 |
1698 | // 删除用户
1699 | function proxmoxlxc_user_del($params){
1700 |
1701 | $request = json_decode(proxmoxlxc_request($params,"/api2/extjs/access/users/".$params['dedicatedip']."@pve","","DELETE"),true);
1702 |
1703 | if($request==null){
1704 | return FALSE;
1705 | }
1706 |
1707 | if($request['success']){
1708 | // 删除成功 开始分配权限
1709 | return true;
1710 |
1711 |
1712 | }
1713 |
1714 |
1715 | }
1716 |
1717 | // 禁用用户
1718 | function proxmoxlxc_user_ban(){
1719 | proxmoxlxc_HardOff($params);
1720 |
1721 | }
1722 |
1723 | // 解除禁用用户
1724 | function proxmoxlxc_user_unban(){
1725 |
1726 | }
1727 |
1728 | // 获取用户访问VNC授权
1729 | function proxmoxlxc_get_ticket($params){
1730 |
1731 | $curl = curl_init();
1732 |
1733 | $url = $params['server_http_prefix']."://".$params['server_ip'].":".$params['port']."/api2/extjs/access/ticket";
1734 | curl_setopt($curl,CURLOPT_URL,$url);
1735 | curl_setopt($curl,CURLOPT_POSTFIELDS,"username=".$params['dedicatedip']."&password=".$params['password']."&realm=pve&new-format=1");
1736 | curl_setopt($curl,CURLOPT_SSL_VERIFYPEER,FALSE); // 屏蔽SSL
1737 | curl_setopt($curl,CURLOPT_SSL_VERIFYHOST,FALSE); // 屏蔽SSL
1738 | curl_setopt($curl, CURLOPT_RETURNTRANSFER,1);
1739 |
1740 | $response = curl_exec($curl);
1741 | $err = curl_error($curl);
1742 |
1743 | curl_close($curl);
1744 |
1745 | if ($err) {
1746 | // 判断服务器请求正常
1747 | return FALSE;
1748 | } else {
1749 |
1750 | $response = json_decode($response,true);
1751 | if($response['success']){
1752 | return $response['data']['ticket'];
1753 | }else{
1754 | return FALSE;
1755 | }
1756 |
1757 | }
1758 |
1759 |
1760 | }
1761 |
1762 | // VNC判断是否存在后端文件
1763 | function proxmoxlxc_vnc_if($params){
1764 | $curl = curl_init();
1765 | $url = $params['server_http_prefix']."://".$params['server_ip'].":".$params['port']."/novnc/mgnovnc.html";
1766 | //$url = $params['server_http_prefix']."://".$params['server_ip'].":".$params['port']."/?console=shell&novnc=1";
1767 | curl_setopt($curl,CURLOPT_URL,$url);
1768 | curl_setopt($curl,CURLOPT_SSL_VERIFYPEER,FALSE); // 屏蔽SSL
1769 | curl_setopt($curl,CURLOPT_SSL_VERIFYHOST,FALSE); // 屏蔽SSL
1770 | curl_setopt($curl, CURLOPT_RETURNTRANSFER,1);
1771 | $response = strval(curl_exec($curl));
1772 | $err = curl_error($curl);
1773 |
1774 | curl_close($curl);
1775 |
1776 |
1777 | if ($err) {
1778 | return FALSE;
1779 | } else {
1780 | // echo $response;
1781 | if(stripos($response,"Loading")){
1782 | return true;
1783 | }else{
1784 | return FALSE;
1785 | }
1786 | // echo "是字符串";
1787 |
1788 | }
1789 |
1790 |
1791 | }
1792 |
1793 | function proxmoxlxc_request($params,$url,$data="",$method='GET'){
1794 |
1795 | $url = $params['server_http_prefix']."://".$params['server_ip'].":".$params['port'].$url;
1796 | if($data!=""){
1797 |
1798 | if($data['no']){
1799 | // 不格式化
1800 |
1801 | /*注意一下!!!如果后续其他功能BOOM了 绝对是这个造成的!!!! */
1802 | unset($data['no']);
1803 |
1804 | $body = json_encode($data);
1805 | }else{
1806 | // 格式化一下要传递的格式
1807 | foreach ($data as $key=>$val) {
1808 | if($body == ""){
1809 | // 首次
1810 | $body = $key."=".$val."&";
1811 | }else{
1812 | $body = $body.$key."=".$val."&";
1813 | }
1814 | }
1815 |
1816 | }
1817 |
1818 |
1819 | }
1820 | // return $url;
1821 | $ch = curl_init(); //初始化CURL句柄
1822 | curl_setopt($ch, CURLOPT_URL, $url); //设置请求的URL
1823 | curl_setopt($ch, CURLOPT_RETURNTRANSFER,1); //设为TRUE把curl_exec()结果转化为字串,而不是直接输出
1824 | curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); //设置请求方式
1825 | curl_setopt($ch,CURLOPT_HTTPHEADER,array("X-HTTP-Method-Override: $method","Authorization:PVEAPIToken=".$params['accesshash']));//设置HTTP头信息 携带TOKEN
1826 | curl_setopt($ch, CURLOPT_POSTFIELDS, $body);//设置提交的字符串
1827 | curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE); // 屏蔽SSL
1828 | curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE); // 屏蔽SSL
1829 | // curl_setopt($ch, CURLOPT_TIMEOUT,$this->Curl_TimeOut);
1830 | $document = curl_exec($ch);//执行预定义的CURL
1831 | $err = curl_error($ch);
1832 | curl_close($ch);
1833 | $r = mb_convert_encoding($document, 'UTF-8','GBK');
1834 |
1835 | // 错误处理
1836 |
1837 | if ($err) {
1838 | return "errorCurl:".$err;
1839 | } else {
1840 | return $r;
1841 | }
1842 |
1843 |
1844 |
1845 | }
1846 |
1847 | ?>
--------------------------------------------------------------------------------