mirror of
https://github.com/lollipopkit/flutter_server_box.git
synced 2025-12-17 23:34:24 +01:00
opt.: pve dashboard (#307)
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import 'package:toolbox/core/extension/context/locale.dart';
|
||||
import 'package:toolbox/core/extension/duration.dart';
|
||||
import 'package:toolbox/core/extension/numx.dart';
|
||||
import 'package:toolbox/core/extension/order.dart';
|
||||
|
||||
enum PveResType {
|
||||
lxc,
|
||||
@@ -61,8 +62,9 @@ sealed class PveResIface {
|
||||
abstract interface class PveCtrlIface {
|
||||
String get node;
|
||||
String get id;
|
||||
bool get isRunning;
|
||||
bool get available;
|
||||
String get summary;
|
||||
String get name;
|
||||
}
|
||||
|
||||
final class PveLxc extends PveResIface implements PveCtrlIface {
|
||||
@@ -73,6 +75,7 @@ final class PveLxc extends PveResIface implements PveCtrlIface {
|
||||
final int vmid;
|
||||
@override
|
||||
final String node;
|
||||
@override
|
||||
final String name;
|
||||
@override
|
||||
final String status;
|
||||
@@ -131,11 +134,11 @@ final class PveLxc extends PveResIface implements PveCtrlIface {
|
||||
}
|
||||
|
||||
@override
|
||||
bool get isRunning => status == 'running';
|
||||
bool get available => status == 'running';
|
||||
|
||||
@override
|
||||
String get summary {
|
||||
if (isRunning) {
|
||||
if (available) {
|
||||
return uptime.secondsToDuration().toStr;
|
||||
}
|
||||
return l10n.stopped;
|
||||
@@ -150,6 +153,7 @@ final class PveQemu extends PveResIface implements PveCtrlIface {
|
||||
final int vmid;
|
||||
@override
|
||||
final String node;
|
||||
@override
|
||||
final String name;
|
||||
@override
|
||||
final String status;
|
||||
@@ -208,11 +212,11 @@ final class PveQemu extends PveResIface implements PveCtrlIface {
|
||||
}
|
||||
|
||||
@override
|
||||
bool get isRunning => status == 'running';
|
||||
bool get available => status == 'running';
|
||||
|
||||
@override
|
||||
String get summary {
|
||||
if (isRunning) {
|
||||
if (available) {
|
||||
return uptime.secondsToDuration().toStr;
|
||||
}
|
||||
return l10n.stopped;
|
||||
@@ -269,12 +273,13 @@ final class PveNode extends PveResIface {
|
||||
}
|
||||
}
|
||||
|
||||
final class PveStorage extends PveResIface {
|
||||
final class PveStorage extends PveResIface implements PveCtrlIface {
|
||||
@override
|
||||
final String id;
|
||||
@override
|
||||
final PveResType type;
|
||||
final String storage;
|
||||
@override
|
||||
final String node;
|
||||
@override
|
||||
final String status;
|
||||
@@ -311,14 +316,29 @@ final class PveStorage extends PveResIface {
|
||||
maxdisk: json['maxdisk'],
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool get available => status == 'available';
|
||||
|
||||
@override
|
||||
String get name => storage;
|
||||
|
||||
@override
|
||||
String get summary {
|
||||
if (available) {
|
||||
return '${l10n.used}: ${disk.bytes2Str} / ${l10n.total}: ${maxdisk.bytes2Str}';
|
||||
}
|
||||
return l10n.notAvailable;
|
||||
}
|
||||
}
|
||||
|
||||
final class PveSdn extends PveResIface {
|
||||
final class PveSdn extends PveResIface implements PveCtrlIface {
|
||||
@override
|
||||
final String id;
|
||||
@override
|
||||
final PveResType type;
|
||||
final String sdn;
|
||||
@override
|
||||
final String node;
|
||||
@override
|
||||
final String status;
|
||||
@@ -340,6 +360,15 @@ final class PveSdn extends PveResIface {
|
||||
status: json['status'],
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool get available => status == 'ok';
|
||||
|
||||
@override
|
||||
String get name => sdn;
|
||||
|
||||
@override
|
||||
String get summary => available ? status : l10n.notAvailable;
|
||||
}
|
||||
|
||||
final class PveRes {
|
||||
@@ -357,6 +386,8 @@ final class PveRes {
|
||||
required this.sdns,
|
||||
});
|
||||
|
||||
bool get onlyOneNode => nodes.length == 1;
|
||||
|
||||
int get length =>
|
||||
qemus.length + lxcs.length + nodes.length + storages.length + sdns.length;
|
||||
|
||||
@@ -379,4 +410,59 @@ final class PveRes {
|
||||
index -= storages.length;
|
||||
return sdns[index];
|
||||
}
|
||||
|
||||
static Future<PveRes> parse((List list, PveRes? old) val) async {
|
||||
final (list, old) = val;
|
||||
final items = list.map((e) => PveResIface.fromJson(e)).toList();
|
||||
final Order<PveQemu> qemus = [];
|
||||
final Order<PveLxc> lxcs = [];
|
||||
final Order<PveNode> nodes = [];
|
||||
final Order<PveStorage> storages = [];
|
||||
final Order<PveSdn> sdns = [];
|
||||
for (final item in items) {
|
||||
switch (item.type) {
|
||||
case PveResType.lxc:
|
||||
lxcs.add(item as PveLxc);
|
||||
break;
|
||||
case PveResType.qemu:
|
||||
qemus.add(item as PveQemu);
|
||||
break;
|
||||
case PveResType.node:
|
||||
nodes.add(item as PveNode);
|
||||
break;
|
||||
case PveResType.storage:
|
||||
storages.add(item as PveStorage);
|
||||
break;
|
||||
case PveResType.sdn:
|
||||
sdns.add(item as PveSdn);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (old != null) {
|
||||
qemus.reorder(
|
||||
order: old.qemus.map((e) => e.id).toList(),
|
||||
finder: (e, s) => e.id == s);
|
||||
lxcs.reorder(
|
||||
order: old.lxcs.map((e) => e.id).toList(),
|
||||
finder: (e, s) => e.id == s);
|
||||
nodes.reorder(
|
||||
order: old.nodes.map((e) => e.id).toList(),
|
||||
finder: (e, s) => e.id == s);
|
||||
storages.reorder(
|
||||
order: old.storages.map((e) => e.id).toList(),
|
||||
finder: (e, s) => e.id == s);
|
||||
sdns.reorder(
|
||||
order: old.sdns.map((e) => e.id).toList(),
|
||||
finder: (e, s) => e.id == s);
|
||||
}
|
||||
|
||||
return PveRes(
|
||||
qemus: qemus,
|
||||
lxcs: lxcs,
|
||||
nodes: nodes,
|
||||
storages: storages,
|
||||
sdns: sdns,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:computer/computer.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:toolbox/core/extension/order.dart';
|
||||
import 'package:toolbox/data/model/server/pve.dart';
|
||||
import 'package:toolbox/data/model/server/server_private_info.dart';
|
||||
import 'package:toolbox/data/res/logger.dart';
|
||||
|
||||
typedef PveCtrlFunc = Future<bool> Function(String node, String id);
|
||||
|
||||
final class PveProvider extends ChangeNotifier {
|
||||
final ServerPrivateInfo spi;
|
||||
late final String addr;
|
||||
//late final SSHClient _client;
|
||||
|
||||
final data = ValueNotifier<PveRes?>(null);
|
||||
|
||||
PveProvider({
|
||||
required this.spi,
|
||||
}) {
|
||||
PveProvider({required this.spi}) {
|
||||
// final client = _spi.server?.client;
|
||||
// if (client == null) {
|
||||
// throw Exception('Server client is null');
|
||||
@@ -27,19 +26,31 @@ final class PveProvider extends ChangeNotifier {
|
||||
return;
|
||||
}
|
||||
this.addr = addr;
|
||||
_init().then((_) => connected.complete());
|
||||
_init();
|
||||
}
|
||||
|
||||
final err = ValueNotifier<String?>(null);
|
||||
final connected = Completer<void>();
|
||||
final session = Dio();
|
||||
final data = ValueNotifier<PveRes?>(null);
|
||||
bool get onlyOneNode => data.value?.nodes.length == 1;
|
||||
String? release;
|
||||
bool isBusy = false;
|
||||
|
||||
// int _localPort = 0;
|
||||
// String get addr => 'http://127.0.0.1:$_localPort';
|
||||
|
||||
Future<void> _init() async {
|
||||
//await _forward();
|
||||
await _login();
|
||||
try {
|
||||
//await _forward();
|
||||
await _login();
|
||||
await _release;
|
||||
} catch (e) {
|
||||
Loggers.app.warning('PVE init failed', e);
|
||||
err.value = e.toString();
|
||||
} finally {
|
||||
connected.complete();
|
||||
}
|
||||
}
|
||||
|
||||
// Future<void> _forward() async {
|
||||
@@ -75,65 +86,30 @@ final class PveProvider extends ChangeNotifier {
|
||||
session.options.headers['Cookie'] = 'PVEAuthCookie=$ticket';
|
||||
}
|
||||
|
||||
Future<PveRes> list() async {
|
||||
/// Returns true if the PVE version is 8.0 or later
|
||||
Future<void> get _release async {
|
||||
final resp = await session.get('$addr/api2/extjs/version');
|
||||
final version = resp.data['data']['release'] as String?;
|
||||
if (version != null) {
|
||||
release = version;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> list() async {
|
||||
await connected.future;
|
||||
final resp = await session.get('$addr/api2/json/cluster/resources');
|
||||
final list = resp.data['data'] as List;
|
||||
final items = list.map((e) => PveResIface.fromJson(e)).toList();
|
||||
|
||||
final Order<PveQemu> qemus = [];
|
||||
final Order<PveLxc> lxcs = [];
|
||||
final Order<PveNode> nodes = [];
|
||||
final Order<PveStorage> storages = [];
|
||||
final Order<PveSdn> sdns = [];
|
||||
for (final item in items) {
|
||||
switch (item.type) {
|
||||
case PveResType.lxc:
|
||||
lxcs.add(item as PveLxc);
|
||||
break;
|
||||
case PveResType.qemu:
|
||||
qemus.add(item as PveQemu);
|
||||
break;
|
||||
case PveResType.node:
|
||||
nodes.add(item as PveNode);
|
||||
break;
|
||||
case PveResType.storage:
|
||||
storages.add(item as PveStorage);
|
||||
break;
|
||||
case PveResType.sdn:
|
||||
sdns.add(item as PveSdn);
|
||||
break;
|
||||
}
|
||||
if (isBusy) return;
|
||||
isBusy = true;
|
||||
try {
|
||||
final resp = await session.get('$addr/api2/json/cluster/resources');
|
||||
final res = resp.data['data'] as List;
|
||||
final result = await Computer.shared.start(PveRes.parse, (res, data.value));
|
||||
data.value = result;
|
||||
} catch (e) {
|
||||
Loggers.app.warning('PVE list failed', e);
|
||||
err.value = e.toString();
|
||||
} finally {
|
||||
isBusy = false;
|
||||
}
|
||||
|
||||
final old = data.value;
|
||||
if (old != null) {
|
||||
qemus.reorder(
|
||||
order: old.qemus.map((e) => e.id).toList(),
|
||||
finder: (e, s) => e.id == s);
|
||||
lxcs.reorder(
|
||||
order: old.lxcs.map((e) => e.id).toList(),
|
||||
finder: (e, s) => e.id == s);
|
||||
nodes.reorder(
|
||||
order: old.nodes.map((e) => e.id).toList(),
|
||||
finder: (e, s) => e.id == s);
|
||||
storages.reorder(
|
||||
order: old.storages.map((e) => e.id).toList(),
|
||||
finder: (e, s) => e.id == s);
|
||||
sdns.reorder(
|
||||
order: old.sdns.map((e) => e.id).toList(),
|
||||
finder: (e, s) => e.id == s);
|
||||
}
|
||||
|
||||
final res = PveRes(
|
||||
qemus: qemus,
|
||||
lxcs: lxcs,
|
||||
nodes: nodes,
|
||||
storages: storages,
|
||||
sdns: sdns,
|
||||
);
|
||||
data.value = res;
|
||||
return res;
|
||||
}
|
||||
|
||||
Future<bool> reboot(String node, String id) async {
|
||||
|
||||
Reference in New Issue
Block a user