feat: add serde derive to Score types
This commit adds `serde` dependency and derives `Serialize` trait for `Score` types. This is necessary for serialization and deserialization of these types, which is required to display Scores to various user interfaces - Added `serde` dependency to `harmony_types/Cargo.toml`. - Added `serde::Serialize` derive macro to `MacAddress` in `harmony_types/src/lib.rs`. - Added `serde::Serialize` derive macro to `Config` in `opnsense-config/src/config/config.rs`. - Added `serde::Serialize` derive macro to `Score` in `harmony_types/src/lib.rs`. - Added `serde::Serialize` derive macro to `Config` and `Score` in relevant modules. - Added placeholder `todo!()` implementations for `serialize` methods. These will be implemented in future commits.
This commit is contained in:
parent
ab9b7476a4
commit
b4cc5cff4f
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -1158,6 +1158,7 @@ dependencies = [
|
||||
"rust-ipmi",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde-value",
|
||||
"serde_json",
|
||||
"serde_yaml",
|
||||
"tokio",
|
||||
@ -1195,6 +1196,9 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "harmony_types"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
|
@ -30,6 +30,7 @@ url = "2.5.4"
|
||||
kube = "0.98.0"
|
||||
k8s-openapi = { version = "0.24.0", features = [ "v1_30" ] }
|
||||
serde_yaml = "0.9.34"
|
||||
serde-value = "0.7.0"
|
||||
http = "1.2.0"
|
||||
|
||||
[workspace.dependencies.uuid]
|
||||
|
@ -2,8 +2,7 @@ use harmony::{
|
||||
data::Version,
|
||||
maestro::Maestro,
|
||||
modules::lamp::{LAMPConfig, LAMPScore},
|
||||
score::Score,
|
||||
topology::{HAClusterTopology, Topology, Url},
|
||||
topology::{HAClusterTopology, Url},
|
||||
};
|
||||
|
||||
#[tokio::main]
|
||||
@ -23,7 +22,3 @@ async fn main() {
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn clone_score<T: Topology, S: Score<T> + Clone + 'static>(score: S) -> Box<S> {
|
||||
Box::new(score.clone())
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
use harmony::{
|
||||
inventory::Inventory,
|
||||
maestro::Maestro,
|
||||
modules::{dummy::{ErrorScore, PanicScore, SuccessScore}, k8s::deployment::K8sDeploymentScore},
|
||||
modules::{
|
||||
dummy::{ErrorScore, PanicScore, SuccessScore},
|
||||
k8s::deployment::K8sDeploymentScore,
|
||||
},
|
||||
topology::HAClusterTopology,
|
||||
};
|
||||
|
||||
|
@ -29,3 +29,4 @@ kube = { workspace = true }
|
||||
k8s-openapi = { workspace = true }
|
||||
serde_yaml = { workspace = true }
|
||||
http = { workspace = true }
|
||||
serde-value = { workspace = true }
|
||||
|
@ -2,6 +2,8 @@ use std::sync::Arc;
|
||||
|
||||
use derive_new::new;
|
||||
use harmony_types::net::MacAddress;
|
||||
use serde::{Serialize, Serializer, ser::SerializeStruct};
|
||||
use serde_value::Value;
|
||||
|
||||
pub type HostGroup = Vec<PhysicalHost>;
|
||||
pub type SwitchGroup = Vec<Switch>;
|
||||
@ -75,10 +77,7 @@ impl PhysicalHost {
|
||||
}
|
||||
|
||||
pub fn label(mut self, name: String, value: String) -> Self {
|
||||
self.labels.push(Label {
|
||||
_name: name,
|
||||
_value: value,
|
||||
});
|
||||
self.labels.push(Label { name, value });
|
||||
self
|
||||
}
|
||||
|
||||
@ -88,7 +87,49 @@ impl PhysicalHost {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(new)]
|
||||
// Custom Serialize implementation for PhysicalHost
|
||||
impl Serialize for PhysicalHost {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
// Determine the number of fields
|
||||
let mut num_fields = 5; // category, network, storage, labels, management
|
||||
if self.memory_size.is_some() {
|
||||
num_fields += 1;
|
||||
}
|
||||
if self.cpu_count.is_some() {
|
||||
num_fields += 1;
|
||||
}
|
||||
|
||||
// Create a serialization structure
|
||||
let mut state = serializer.serialize_struct("PhysicalHost", num_fields)?;
|
||||
|
||||
// Serialize the standard fields
|
||||
state.serialize_field("category", &self.category)?;
|
||||
state.serialize_field("network", &self.network)?;
|
||||
state.serialize_field("storage", &self.storage)?;
|
||||
state.serialize_field("labels", &self.labels)?;
|
||||
|
||||
// Serialize optional fields
|
||||
if let Some(memory) = self.memory_size {
|
||||
state.serialize_field("memory_size", &memory)?;
|
||||
}
|
||||
if let Some(cpu) = self.cpu_count {
|
||||
state.serialize_field("cpu_count", &cpu)?;
|
||||
}
|
||||
|
||||
let mgmt_data = self.management.serialize_management();
|
||||
// pub management: Arc<dyn ManagementInterface>,
|
||||
|
||||
// Handle management interface - either as a field or flattened
|
||||
state.serialize_field("management", &mgmt_data)?;
|
||||
|
||||
state.end()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(new, Serialize)]
|
||||
pub struct ManualManagementInterface;
|
||||
|
||||
impl ManagementInterface for ManualManagementInterface {
|
||||
@ -101,7 +142,7 @@ impl ManagementInterface for ManualManagementInterface {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ManagementInterface: Send + Sync {
|
||||
pub trait ManagementInterface: Send + Sync + SerializableManagement {
|
||||
fn boot_to_pxe(&self);
|
||||
fn get_supported_protocol_names(&self) -> String;
|
||||
}
|
||||
@ -115,21 +156,49 @@ impl std::fmt::Debug for dyn ManagementInterface {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
// Define a trait for serializing management interfaces
|
||||
pub trait SerializableManagement {
|
||||
fn serialize_management(&self) -> Value;
|
||||
}
|
||||
|
||||
// Provide a blanket implementation for all types that implement both ManagementInterface and Serialize
|
||||
impl<T> SerializableManagement for T
|
||||
where
|
||||
T: ManagementInterface + Serialize,
|
||||
{
|
||||
fn serialize_management(&self) -> Value {
|
||||
serde_value::to_value(self).expect("ManagementInterface should serialize successfully")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub enum HostCategory {
|
||||
Server,
|
||||
Firewall,
|
||||
Switch,
|
||||
}
|
||||
|
||||
#[derive(Debug, new, Clone)]
|
||||
#[derive(Debug, new, Clone, Serialize)]
|
||||
pub struct NetworkInterface {
|
||||
pub name: Option<String>,
|
||||
pub mac_address: MacAddress,
|
||||
pub speed: Option<u64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, new, Clone)]
|
||||
#[cfg(test)]
|
||||
use harmony_macros::mac_address;
|
||||
#[cfg(test)]
|
||||
impl NetworkInterface {
|
||||
pub fn dummy() -> Self {
|
||||
Self {
|
||||
name: Some(String::new()),
|
||||
mac_address: mac_address!("00:00:00:00:00:00"),
|
||||
speed: Some(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, new, Clone, Serialize)]
|
||||
pub enum StorageConnectionType {
|
||||
Sata3g,
|
||||
Sata6g,
|
||||
@ -137,13 +206,13 @@ pub enum StorageConnectionType {
|
||||
Sas12g,
|
||||
PCIE,
|
||||
}
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub enum StorageKind {
|
||||
SSD,
|
||||
NVME,
|
||||
HDD,
|
||||
}
|
||||
#[derive(Debug, new, Clone)]
|
||||
#[derive(Debug, new, Clone, Serialize)]
|
||||
pub struct Storage {
|
||||
pub connection: StorageConnectionType,
|
||||
pub kind: StorageKind,
|
||||
@ -151,20 +220,33 @@ pub struct Storage {
|
||||
pub serial: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg(test)]
|
||||
impl Storage {
|
||||
pub fn dummy() -> Self {
|
||||
Self {
|
||||
connection: StorageConnectionType::Sata3g,
|
||||
kind: StorageKind::SSD,
|
||||
size: 0,
|
||||
serial: String::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct Switch {
|
||||
_interface: Vec<NetworkInterface>,
|
||||
_management_interface: NetworkInterface,
|
||||
}
|
||||
|
||||
#[derive(Debug, new, Clone)]
|
||||
#[derive(Debug, new, Clone, Serialize)]
|
||||
pub struct Label {
|
||||
_name: String,
|
||||
_value: String,
|
||||
pub name: String,
|
||||
pub value: String,
|
||||
}
|
||||
|
||||
pub type Address = String;
|
||||
|
||||
#[derive(new, Debug)]
|
||||
#[derive(new, Debug, Serialize)]
|
||||
pub struct Location {
|
||||
pub address: Address,
|
||||
pub name: String,
|
||||
@ -178,3 +260,158 @@ impl Location {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::sync::Arc;
|
||||
|
||||
// Mock implementation of ManagementInterface
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
struct MockHPIlo {
|
||||
ip: String,
|
||||
username: String,
|
||||
password: String,
|
||||
firmware_version: String,
|
||||
}
|
||||
|
||||
impl ManagementInterface for MockHPIlo {
|
||||
fn boot_to_pxe(&self) {}
|
||||
|
||||
fn get_supported_protocol_names(&self) -> String {
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
|
||||
// Another mock implementation
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
struct MockDellIdrac {
|
||||
hostname: String,
|
||||
port: u16,
|
||||
api_token: String,
|
||||
}
|
||||
|
||||
impl ManagementInterface for MockDellIdrac {
|
||||
fn boot_to_pxe(&self) {}
|
||||
|
||||
fn get_supported_protocol_names(&self) -> String {
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serialize_physical_host_with_hp_ilo() {
|
||||
// Create a PhysicalHost with HP iLO management
|
||||
let host = PhysicalHost {
|
||||
category: HostCategory::Server,
|
||||
network: vec![NetworkInterface::dummy()],
|
||||
management: Arc::new(MockHPIlo {
|
||||
ip: "192.168.1.100".to_string(),
|
||||
username: "admin".to_string(),
|
||||
password: "password123".to_string(),
|
||||
firmware_version: "2.5.0".to_string(),
|
||||
}),
|
||||
storage: vec![Storage::dummy()],
|
||||
labels: vec![Label::new("datacenter".to_string(), "us-east".to_string())],
|
||||
memory_size: Some(64_000_000),
|
||||
cpu_count: Some(16),
|
||||
};
|
||||
|
||||
// Serialize to JSON
|
||||
let json = serde_json::to_string(&host).expect("Failed to serialize host");
|
||||
|
||||
// Check that the serialized JSON contains the HP iLO details
|
||||
assert!(json.contains("192.168.1.100"));
|
||||
assert!(json.contains("admin"));
|
||||
assert!(json.contains("password123"));
|
||||
assert!(json.contains("firmware_version"));
|
||||
assert!(json.contains("2.5.0"));
|
||||
|
||||
// Parse back to verify structure (not the exact management interface)
|
||||
let parsed: serde_json::Value = serde_json::from_str(&json).expect("Failed to parse JSON");
|
||||
|
||||
// Verify basic structure
|
||||
assert_eq!(parsed["cpu_count"], 16);
|
||||
assert_eq!(parsed["memory_size"], 64_000_000);
|
||||
assert_eq!(parsed["network"][0]["name"], "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serialize_physical_host_with_dell_idrac() {
|
||||
// Create a PhysicalHost with Dell iDRAC management
|
||||
let host = PhysicalHost {
|
||||
category: HostCategory::Server,
|
||||
network: vec![NetworkInterface::dummy()],
|
||||
management: Arc::new(MockDellIdrac {
|
||||
hostname: "idrac-server01".to_string(),
|
||||
port: 443,
|
||||
api_token: "abcdef123456".to_string(),
|
||||
}),
|
||||
storage: vec![Storage::dummy()],
|
||||
labels: vec![Label::new("env".to_string(), "production".to_string())],
|
||||
memory_size: Some(128_000_000),
|
||||
cpu_count: Some(32),
|
||||
};
|
||||
|
||||
// Serialize to JSON
|
||||
let json = serde_json::to_string(&host).expect("Failed to serialize host");
|
||||
|
||||
// Check that the serialized JSON contains the Dell iDRAC details
|
||||
assert!(json.contains("idrac-server01"));
|
||||
assert!(json.contains("443"));
|
||||
assert!(json.contains("abcdef123456"));
|
||||
|
||||
// Parse back to verify structure
|
||||
let parsed: serde_json::Value = serde_json::from_str(&json).expect("Failed to parse JSON");
|
||||
|
||||
// Verify basic structure
|
||||
assert_eq!(parsed["cpu_count"], 32);
|
||||
assert_eq!(parsed["memory_size"], 128_000_000);
|
||||
assert_eq!(parsed["storage"][0]["path"], serde_json::Value::Null);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_different_management_implementations_produce_valid_json() {
|
||||
// Create hosts with different management implementations
|
||||
let host1 = PhysicalHost {
|
||||
category: HostCategory::Server,
|
||||
network: vec![],
|
||||
management: Arc::new(MockHPIlo {
|
||||
ip: "10.0.0.1".to_string(),
|
||||
username: "root".to_string(),
|
||||
password: "secret".to_string(),
|
||||
firmware_version: "3.0.0".to_string(),
|
||||
}),
|
||||
storage: vec![],
|
||||
labels: vec![],
|
||||
memory_size: None,
|
||||
cpu_count: None,
|
||||
};
|
||||
|
||||
let host2 = PhysicalHost {
|
||||
category: HostCategory::Server,
|
||||
network: vec![],
|
||||
management: Arc::new(MockDellIdrac {
|
||||
hostname: "server02-idrac".to_string(),
|
||||
port: 8443,
|
||||
api_token: "token123".to_string(),
|
||||
}),
|
||||
storage: vec![],
|
||||
labels: vec![],
|
||||
memory_size: None,
|
||||
cpu_count: None,
|
||||
};
|
||||
|
||||
// Both should serialize successfully
|
||||
let json1 = serde_json::to_string(&host1).expect("Failed to serialize host1");
|
||||
let json2 = serde_json::to_string(&host2).expect("Failed to serialize host2");
|
||||
|
||||
// Both JSONs should be valid and parseable
|
||||
let _: serde_json::Value = serde_json::from_str(&json1).expect("Invalid JSON for host1");
|
||||
let _: serde_json::Value = serde_json::from_str(&json2).expect("Invalid JSON for host2");
|
||||
|
||||
// The JSONs should be different because they contain different management interfaces
|
||||
assert_ne!(json1, json2);
|
||||
}
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ impl std::fmt::Display for InterpretName {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait Interpret<T: Topology>: std::fmt::Debug + Send {
|
||||
pub trait Interpret<T>: std::fmt::Debug + Send {
|
||||
async fn execute(&self, inventory: &Inventory, topology: &T)
|
||||
-> Result<Outcome, InterpretError>;
|
||||
fn get_name(&self) -> InterpretName;
|
||||
|
@ -1,15 +1,35 @@
|
||||
use serde::Serialize;
|
||||
use serde_value::Value;
|
||||
|
||||
use super::{interpret::Interpret, topology::Topology};
|
||||
|
||||
pub trait Score<T: Topology>: std::fmt::Debug + Send + Sync + CloneBoxScore<T> {
|
||||
pub trait Score<T: Topology>:
|
||||
std::fmt::Debug + Send + Sync + CloneBoxScore<T> + SerializeScore<T>
|
||||
{
|
||||
fn create_interpret(&self) -> Box<dyn Interpret<T>>;
|
||||
fn name(&self) -> String;
|
||||
}
|
||||
|
||||
pub trait SerializeScore<T: Topology> {
|
||||
fn serialize(&self) -> Value;
|
||||
}
|
||||
|
||||
impl<'de, S, T> SerializeScore<T> for S
|
||||
where
|
||||
T: Topology,
|
||||
S: Score<T> + Serialize,
|
||||
{
|
||||
fn serialize(&self) -> Value {
|
||||
// TODO not sure if this is the right place to handle the error or it should bubble
|
||||
// up?
|
||||
serde_value::to_value(&self).expect("Score should serialize successfully")
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CloneBoxScore<T: Topology> {
|
||||
fn clone_box(&self) -> Box<dyn Score<T>>;
|
||||
}
|
||||
|
||||
|
||||
impl<S, T> CloneBoxScore<T> for S
|
||||
where
|
||||
T: Topology,
|
||||
@ -19,5 +39,3 @@ where
|
||||
Box::new(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait FrontendScore<T: Topology>: Score<T> + std::fmt::Display {}
|
||||
|
@ -334,7 +334,6 @@ impl TftpServer for DummyInfra {
|
||||
#[async_trait]
|
||||
impl HttpServer for DummyInfra {
|
||||
async fn serve_files(&self, _url: &Url) -> Result<(), ExecutorError> {
|
||||
|
||||
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||
}
|
||||
fn get_ip(&self) -> IpAddress {
|
||||
|
@ -1,4 +1,5 @@
|
||||
use derive_new::new;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::hardware::PhysicalHost;
|
||||
|
||||
@ -8,7 +9,7 @@ use super::LogicalHost;
|
||||
///
|
||||
/// This is the only construct that directly maps a logical host to a physical host.
|
||||
/// It serves as a bridge between the logical cluster structure and the physical infrastructure.
|
||||
#[derive(Debug, new, Clone)]
|
||||
#[derive(Debug, new, Clone, Serialize)]
|
||||
pub struct HostBinding {
|
||||
/// Reference to the LogicalHost
|
||||
pub logical_host: LogicalHost,
|
||||
|
@ -2,6 +2,7 @@ use std::{net::SocketAddr, str::FromStr};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use log::debug;
|
||||
use serde::Serialize;
|
||||
|
||||
use super::{IpAddress, LogicalHost};
|
||||
use crate::executors::ExecutorError;
|
||||
@ -36,20 +37,20 @@ impl std::fmt::Debug for dyn LoadBalancer {
|
||||
f.write_fmt(format_args!("LoadBalancer {}", self.get_ip()))
|
||||
}
|
||||
}
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[derive(Debug, PartialEq, Clone, Serialize)]
|
||||
pub struct LoadBalancerService {
|
||||
pub backend_servers: Vec<BackendServer>,
|
||||
pub listening_port: SocketAddr,
|
||||
pub health_check: Option<HealthCheck>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[derive(Debug, PartialEq, Clone, Serialize)]
|
||||
pub struct BackendServer {
|
||||
pub address: String,
|
||||
pub port: u16,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq, Serialize)]
|
||||
pub enum HttpMethod {
|
||||
GET,
|
||||
POST,
|
||||
@ -91,14 +92,14 @@ impl std::fmt::Display for HttpMethod {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq, Serialize)]
|
||||
pub enum HttpStatusCode {
|
||||
Success2xx,
|
||||
UserError4xx,
|
||||
ServerError5xx,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq, Serialize)]
|
||||
pub enum HealthCheck {
|
||||
HTTP(String, HttpMethod, HttpStatusCode),
|
||||
TCP(Option<u16>),
|
||||
|
@ -12,6 +12,7 @@ mod network;
|
||||
pub use host_binding::*;
|
||||
pub use http::*;
|
||||
pub use network::*;
|
||||
use serde::Serialize;
|
||||
pub use tftp::*;
|
||||
|
||||
use std::net::IpAddr;
|
||||
@ -20,8 +21,6 @@ pub trait Topology {
|
||||
fn name(&self) -> &str;
|
||||
}
|
||||
|
||||
pub trait Capability {}
|
||||
|
||||
pub type IpAddress = IpAddr;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@ -30,6 +29,18 @@ pub enum Url {
|
||||
Url(url::Url),
|
||||
}
|
||||
|
||||
impl Serialize for Url {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
match self {
|
||||
Url::LocalFolder(path) => serializer.serialize_str(path),
|
||||
Url::Url(url) => serializer.serialize_str(&url.as_str()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Url {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
@ -48,7 +59,7 @@ impl std::fmt::Display for Url {
|
||||
/// - A control plane node
|
||||
///
|
||||
/// This abstraction focuses on the logical role and services, independent of the physical hardware.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct LogicalHost {
|
||||
/// The IP address of this logical host.
|
||||
pub ip: IpAddress,
|
||||
@ -130,3 +141,23 @@ fn increment_ip(ip: IpAddress, increment: u32) -> Option<IpAddress> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use serde_json;
|
||||
|
||||
#[test]
|
||||
fn test_serialize_local_folder() {
|
||||
let url = Url::LocalFolder("path/to/folder".to_string());
|
||||
let serialized = serde_json::to_string(&url).unwrap();
|
||||
assert_eq!(serialized, "\"path/to/folder\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serialize_url() {
|
||||
let url = Url::Url(url::Url::parse("https://example.com").unwrap());
|
||||
let serialized = serde_json::to_string(&url).unwrap();
|
||||
assert_eq!(serialized, "\"https://example.com/\"");
|
||||
}
|
||||
}
|
||||
|
@ -2,10 +2,11 @@ use std::{net::Ipv4Addr, str::FromStr, sync::Arc};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use harmony_types::net::MacAddress;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::executors::ExecutorError;
|
||||
|
||||
use super::{openshift::OpenshiftClient, IpAddress, LogicalHost};
|
||||
use super::{IpAddress, LogicalHost, openshift::OpenshiftClient};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DHCPStaticEntry {
|
||||
@ -41,11 +42,10 @@ pub struct NetworkDomain {
|
||||
pub name: String,
|
||||
}
|
||||
#[async_trait]
|
||||
pub trait OcK8sclient: Send + Sync + std::fmt::Debug {
|
||||
pub trait OcK8sclient: Send + Sync + std::fmt::Debug {
|
||||
async fn oc_client(&self) -> Result<Arc<OpenshiftClient>, kube::Error>;
|
||||
}
|
||||
|
||||
|
||||
#[async_trait]
|
||||
pub trait DhcpServer: Send + Sync + std::fmt::Debug {
|
||||
async fn add_static_mapping(&self, entry: &DHCPStaticEntry) -> Result<(), ExecutorError>;
|
||||
@ -62,11 +62,7 @@ pub trait DhcpServer: Send + Sync + std::fmt::Debug {
|
||||
pub trait DnsServer: Send + Sync {
|
||||
async fn register_dhcp_leases(&self, register: bool) -> Result<(), ExecutorError>;
|
||||
async fn register_hosts(&self, hosts: Vec<DnsRecord>) -> Result<(), ExecutorError>;
|
||||
fn remove_record(
|
||||
&self,
|
||||
name: &str,
|
||||
record_type: DnsRecordType,
|
||||
) -> Result<(), ExecutorError>;
|
||||
fn remove_record(&self, name: &str, record_type: DnsRecordType) -> Result<(), ExecutorError>;
|
||||
async fn list_records(&self) -> Vec<DnsRecord>;
|
||||
fn get_ip(&self) -> IpAddress;
|
||||
fn get_host(&self) -> LogicalHost;
|
||||
@ -117,7 +113,7 @@ pub enum Action {
|
||||
Deny,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
|
||||
pub enum DnsRecordType {
|
||||
A,
|
||||
AAAA,
|
||||
@ -138,7 +134,7 @@ impl std::fmt::Display for DnsRecordType {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
|
||||
pub struct DnsRecord {
|
||||
pub host: String,
|
||||
pub domain: String,
|
||||
|
@ -3,8 +3,9 @@ use crate::topology::IpAddress;
|
||||
use derive_new::new;
|
||||
use harmony_types::net::MacAddress;
|
||||
use log::info;
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(new)]
|
||||
#[derive(new, Serialize)]
|
||||
pub struct HPIlo {
|
||||
ip_address: Option<IpAddress>,
|
||||
mac_address: Option<MacAddress>,
|
||||
|
@ -2,8 +2,9 @@ use crate::hardware::ManagementInterface;
|
||||
use derive_new::new;
|
||||
use harmony_types::net::MacAddress;
|
||||
use log::info;
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(new)]
|
||||
#[derive(new, Serialize)]
|
||||
pub struct IntelAmtManagement {
|
||||
mac_address: MacAddress,
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
use crate::hardware::ManagementInterface;
|
||||
use derive_new::new;
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(new)]
|
||||
#[derive(new, Serialize)]
|
||||
pub struct OPNSenseManagementInterface {}
|
||||
|
||||
impl ManagementInterface for OPNSenseManagementInterface {
|
||||
|
@ -1,7 +1,7 @@
|
||||
|
||||
use async_trait::async_trait;
|
||||
use derive_new::new;
|
||||
use log::info;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{
|
||||
domain::{data::Version, interpret::InterpretStatus},
|
||||
@ -12,7 +12,7 @@ use crate::{
|
||||
|
||||
use crate::domain::score::Score;
|
||||
|
||||
#[derive(Debug, new, Clone)]
|
||||
#[derive(Debug, new, Clone, Serialize)]
|
||||
pub struct DhcpScore {
|
||||
pub host_binding: Vec<HostBinding>,
|
||||
pub next_server: Option<IpAddress>,
|
||||
@ -134,7 +134,7 @@ impl DhcpInterpret {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<T: Topology + DhcpServer> Interpret<T> for DhcpInterpret {
|
||||
impl<T: DhcpServer> Interpret<T> for DhcpInterpret {
|
||||
fn get_name(&self) -> InterpretName {
|
||||
InterpretName::OPNSenseDHCP
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
use async_trait::async_trait;
|
||||
use derive_new::new;
|
||||
use log::info;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{
|
||||
data::Version,
|
||||
@ -10,7 +11,7 @@ use crate::{
|
||||
topology::{DnsRecord, DnsServer, Topology},
|
||||
};
|
||||
|
||||
#[derive(Debug, new, Clone)]
|
||||
#[derive(Debug, new, Clone, Serialize)]
|
||||
pub struct DnsScore {
|
||||
dns_entries: Vec<DnsRecord>,
|
||||
register_dhcp_leases: Option<bool>,
|
||||
|
@ -1,4 +1,5 @@
|
||||
use async_trait::async_trait;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{
|
||||
data::Version,
|
||||
@ -10,7 +11,7 @@ use crate::{
|
||||
|
||||
/// Score that always errors. This is only useful for development/testing purposes. It does nothing
|
||||
/// except returning Err(InterpretError) when interpreted.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct ErrorScore;
|
||||
|
||||
impl<T: Topology> Score<T> for ErrorScore {
|
||||
@ -28,7 +29,7 @@ impl<T: Topology> Score<T> for ErrorScore {
|
||||
|
||||
/// Score that always succeeds. This is only useful for development/testing purposes. It does nothing
|
||||
/// except returning Ok(Outcome::success) when interpreted.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct SuccessScore;
|
||||
|
||||
impl<T: Topology> Score<T> for SuccessScore {
|
||||
@ -81,7 +82,7 @@ impl<T: Topology> Interpret<T> for DummyInterpret {
|
||||
|
||||
/// Score that always panics. This is only useful for development/testing purposes. It does nothing
|
||||
/// except panic! with an error message when interpreted
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct PanicScore;
|
||||
|
||||
impl<T: Topology> Score<T> for PanicScore {
|
||||
|
@ -1,5 +1,6 @@
|
||||
use async_trait::async_trait;
|
||||
use derive_new::new;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{
|
||||
data::{Id, Version},
|
||||
@ -9,7 +10,7 @@ use crate::{
|
||||
topology::{HttpServer, Topology, Url},
|
||||
};
|
||||
|
||||
#[derive(Debug, new, Clone)]
|
||||
#[derive(Debug, new, Clone, Serialize)]
|
||||
pub struct HttpScore {
|
||||
files_to_serve: Url,
|
||||
}
|
||||
|
@ -1,17 +1,22 @@
|
||||
use k8s_openapi::api::apps::v1::Deployment;
|
||||
use serde::Serialize;
|
||||
use serde_json::json;
|
||||
|
||||
use crate::{interpret::Interpret, score::Score, topology::{OcK8sclient, Topology}};
|
||||
use crate::{
|
||||
interpret::Interpret,
|
||||
score::Score,
|
||||
topology::{OcK8sclient, Topology},
|
||||
};
|
||||
|
||||
use super::resource::{K8sResourceInterpret, K8sResourceScore};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct K8sDeploymentScore {
|
||||
pub name: String,
|
||||
pub image: String,
|
||||
}
|
||||
|
||||
impl <T:Topology + OcK8sclient> Score<T> for K8sDeploymentScore {
|
||||
impl<T: Topology + OcK8sclient> Score<T> for K8sDeploymentScore {
|
||||
fn create_interpret(&self) -> Box<dyn Interpret<T>> {
|
||||
let deployment: Deployment = serde_json::from_value(json!(
|
||||
{
|
||||
|
@ -1,7 +1,7 @@
|
||||
use async_trait::async_trait;
|
||||
use k8s_openapi::NamespaceResourceScope;
|
||||
use kube::Resource;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::{Serialize, de::DeserializeOwned};
|
||||
|
||||
use crate::{
|
||||
data::{Id, Version},
|
||||
@ -11,7 +11,7 @@ use crate::{
|
||||
topology::{OcK8sclient, Topology},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct K8sResourceScore<K: Resource + std::fmt::Debug> {
|
||||
pub resource: Vec<K>,
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{
|
||||
data::{Id, Version},
|
||||
@ -11,7 +12,7 @@ use crate::{
|
||||
topology::{OcK8sclient, Topology, Url},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct LAMPScore {
|
||||
pub name: String,
|
||||
pub domain: Url,
|
||||
@ -19,7 +20,7 @@ pub struct LAMPScore {
|
||||
pub php_version: Version,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct LAMPConfig {
|
||||
pub project_root: PathBuf,
|
||||
pub ssl_enabled: bool,
|
||||
@ -34,7 +35,7 @@ impl Default for LAMPConfig {
|
||||
}
|
||||
}
|
||||
|
||||
impl <T:Topology> Score<T> for LAMPScore {
|
||||
impl<T: Topology> Score<T> for LAMPScore {
|
||||
fn create_interpret(&self) -> Box<dyn Interpret<T>> {
|
||||
todo!()
|
||||
}
|
||||
@ -50,7 +51,7 @@ pub struct LAMPInterpret {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl <T:Topology + OcK8sclient> Interpret<T> for LAMPInterpret {
|
||||
impl<T: Topology + OcK8sclient> Interpret<T> for LAMPInterpret {
|
||||
async fn execute(
|
||||
&self,
|
||||
inventory: &Inventory,
|
||||
|
@ -1,15 +1,16 @@
|
||||
use async_trait::async_trait;
|
||||
use log::info;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{
|
||||
data::{Id, Version},
|
||||
interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome},
|
||||
inventory::Inventory,
|
||||
score::{FrontendScore, Score},
|
||||
score::Score,
|
||||
topology::{LoadBalancer, LoadBalancerService, Topology},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct LoadBalancerScore {
|
||||
pub public_services: Vec<LoadBalancerService>,
|
||||
pub private_services: Vec<LoadBalancerService>,
|
||||
@ -19,8 +20,6 @@ pub struct LoadBalancerScore {
|
||||
// uuid?
|
||||
}
|
||||
|
||||
impl <T: Topology + LoadBalancer> FrontendScore<T> for LoadBalancerScore {}
|
||||
|
||||
impl<T: Topology + LoadBalancer> Score<T> for LoadBalancerScore {
|
||||
fn create_interpret(&self) -> Box<dyn Interpret<T>> {
|
||||
Box::new(LoadBalancerInterpret::new(self.clone()))
|
||||
|
@ -3,8 +3,8 @@ pub mod dns;
|
||||
pub mod dummy;
|
||||
pub mod http;
|
||||
pub mod k8s;
|
||||
pub mod lamp;
|
||||
pub mod load_balancer;
|
||||
pub mod okd;
|
||||
pub mod opnsense;
|
||||
pub mod tftp;
|
||||
pub mod lamp;
|
||||
|
@ -1,3 +1,5 @@
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{
|
||||
interpret::Interpret,
|
||||
inventory::Inventory,
|
||||
@ -6,7 +8,7 @@ use crate::{
|
||||
topology::{DhcpServer, HAClusterTopology, HostBinding, Topology},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct OKDBootstrapDhcpScore {
|
||||
dhcp_score: DhcpScore,
|
||||
}
|
||||
|
@ -1,15 +1,18 @@
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{
|
||||
interpret::Interpret,
|
||||
modules::load_balancer::LoadBalancerScore,
|
||||
score::Score,
|
||||
topology::{
|
||||
BackendServer, HAClusterTopology, HealthCheck, HttpMethod, HttpStatusCode, LoadBalancer, LoadBalancerService, Topology
|
||||
BackendServer, HAClusterTopology, HealthCheck, HttpMethod, HttpStatusCode, LoadBalancer,
|
||||
LoadBalancerService, Topology,
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct OKDBootstrapLoadBalancerScore {
|
||||
load_balancer_score: LoadBalancerScore,
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{
|
||||
interpret::Interpret,
|
||||
inventory::Inventory,
|
||||
@ -6,7 +8,7 @@ use crate::{
|
||||
topology::{DhcpServer, HAClusterTopology, HostBinding, Topology},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct OKDDhcpScore {
|
||||
dhcp_score: DhcpScore,
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{
|
||||
interpret::Interpret,
|
||||
modules::dns::DnsScore,
|
||||
@ -5,7 +7,7 @@ use crate::{
|
||||
topology::{DnsRecord, DnsRecordType, DnsServer, HAClusterTopology, Topology},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct OKDDnsScore {
|
||||
dns_score: DnsScore,
|
||||
}
|
||||
|
@ -1,11 +1,14 @@
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{
|
||||
interpret::Interpret,
|
||||
modules::load_balancer::LoadBalancerScore,
|
||||
score::{FrontendScore, Score},
|
||||
score::Score,
|
||||
topology::{
|
||||
BackendServer, HAClusterTopology, HealthCheck, HttpMethod, HttpStatusCode, LoadBalancer, LoadBalancerService, Topology
|
||||
BackendServer, HAClusterTopology, HealthCheck, HttpMethod, HttpStatusCode, LoadBalancer,
|
||||
LoadBalancerService, Topology,
|
||||
},
|
||||
};
|
||||
|
||||
@ -15,9 +18,7 @@ impl std::fmt::Display for OKDLoadBalancerScore {
|
||||
}
|
||||
}
|
||||
|
||||
impl <T: Topology + LoadBalancer> FrontendScore<T> for OKDLoadBalancerScore {}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct OKDLoadBalancerScore {
|
||||
load_balancer_score: LoadBalancerScore,
|
||||
}
|
||||
|
@ -1,6 +1,4 @@
|
||||
|
||||
mod shell;
|
||||
mod upgrade;
|
||||
pub use shell::*;
|
||||
pub use upgrade::*;
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use serde::Serialize;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::{
|
||||
@ -13,10 +14,27 @@ use crate::{
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct OPNsenseShellCommandScore {
|
||||
// TODO I am pretty sure we should not hold a direct reference to the
|
||||
// opnsense_config::Config here.
|
||||
// This causes a problem with serialization but also could cause many more problems as this
|
||||
// is mixing concerns of configuration (which is the Responsibility of Scores to define)
|
||||
// and state/execution which is the responsibility of interprets via topologies to manage
|
||||
//
|
||||
// I feel like a better solution would be for this Score/Interpret to require
|
||||
// Topology + OPNSenseShell trait bindings
|
||||
pub opnsense: Arc<RwLock<opnsense_config::Config>>,
|
||||
pub command: String,
|
||||
}
|
||||
|
||||
impl Serialize for OPNsenseShellCommandScore {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
todo!("See comment about moving opnsense_config::Config outside the score")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Topology> Score<T> for OPNsenseShellCommandScore {
|
||||
fn create_interpret(&self) -> Box<dyn Interpret<T>> {
|
||||
Box::new(OPNsenseShellInterpret {
|
||||
@ -37,7 +55,7 @@ pub struct OPNsenseShellInterpret {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl <T:Topology> Interpret<T> for OPNsenseShellInterpret {
|
||||
impl<T: Topology> Interpret<T> for OPNsenseShellInterpret {
|
||||
async fn execute(
|
||||
&self,
|
||||
_inventory: &Inventory,
|
||||
|
@ -1,5 +1,6 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use serde::Serialize;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::{
|
||||
@ -15,6 +16,15 @@ pub struct OPNSenseLaunchUpgrade {
|
||||
pub opnsense: Arc<RwLock<opnsense_config::Config>>,
|
||||
}
|
||||
|
||||
impl Serialize for OPNSenseLaunchUpgrade {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
todo!("See comment in OPNSenseShellCommandScore and apply the same idea here")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Topology> Score<T> for OPNSenseLaunchUpgrade {
|
||||
fn create_interpret(&self) -> Box<dyn Interpret<T>> {
|
||||
let score = OPNsenseShellCommandScore {
|
||||
|
@ -1,5 +1,6 @@
|
||||
use async_trait::async_trait;
|
||||
use derive_new::new;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{
|
||||
data::{Id, Version},
|
||||
@ -9,12 +10,12 @@ use crate::{
|
||||
topology::{Router, TftpServer, Topology, Url},
|
||||
};
|
||||
|
||||
#[derive(Debug, new, Clone)]
|
||||
#[derive(Debug, new, Clone, Serialize)]
|
||||
pub struct TftpScore {
|
||||
files_to_serve: Url,
|
||||
}
|
||||
|
||||
impl <T:Topology + TftpServer + Router> Score<T> for TftpScore {
|
||||
impl<T: Topology + TftpServer + Router> Score<T> for TftpScore {
|
||||
fn create_interpret(&self) -> Box<dyn Interpret<T>> {
|
||||
Box::new(TftpInterpret::new(self.clone()))
|
||||
}
|
||||
@ -30,7 +31,7 @@ pub struct TftpInterpret {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl <T:Topology + TftpServer + Router> Interpret<T> for TftpInterpret {
|
||||
impl<T: Topology + TftpServer + Router> Interpret<T> for TftpInterpret {
|
||||
async fn execute(
|
||||
&self,
|
||||
_inventory: &Inventory,
|
||||
|
@ -1,10 +1,13 @@
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use crossterm::event::{Event, KeyCode, KeyEventKind};
|
||||
use harmony::{modules::okd::load_balancer::OKDLoadBalancerScore, score::Score, topology::{LoadBalancer, Topology}};
|
||||
use harmony::{score::Score, topology::Topology};
|
||||
use log::{info, warn};
|
||||
use ratatui::{
|
||||
layout::Rect, style::{Style, Stylize}, widgets::{List, ListItem, ListState, StatefulWidget, Widget}, Frame
|
||||
Frame,
|
||||
layout::Rect,
|
||||
style::{Style, Stylize},
|
||||
widgets::{List, ListItem, ListState, StatefulWidget, Widget},
|
||||
};
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
@ -23,19 +26,20 @@ struct Execution<T: Topology> {
|
||||
score: Box<dyn Score<T>>,
|
||||
}
|
||||
|
||||
impl <T: Topology + LoadBalancer> FrontendScore<T> for OKDLoadBalancerScore {}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ScoreListWidget<T: Topology> {
|
||||
list_state: Arc<RwLock<ListState>>,
|
||||
scores: Vec<Box<dyn FrontendScore<T>>>,
|
||||
scores: Vec<Box<dyn Score<T>>>,
|
||||
execution: Option<Execution<T>>,
|
||||
execution_history: Vec<Execution<T>>,
|
||||
sender: mpsc::Sender<HarmonyTuiEvent<T>>,
|
||||
}
|
||||
|
||||
impl<T: Topology + std::fmt::Debug> ScoreListWidget<T> {
|
||||
pub(crate) fn new(scores: Vec<Box<dyn Score<T>>>, sender: mpsc::Sender<HarmonyTuiEvent<T>>) -> Self {
|
||||
pub(crate) fn new(
|
||||
scores: Vec<Box<dyn Score<T>>>,
|
||||
sender: mpsc::Sender<HarmonyTuiEvent<T>>,
|
||||
) -> Self {
|
||||
let mut list_state = ListState::default();
|
||||
list_state.select_first();
|
||||
let list_state = Arc::new(RwLock::new(list_state));
|
||||
@ -141,4 +145,3 @@ impl<T: Topology> Widget for &ScoreListWidget<T> {
|
||||
fn score_to_list_item<'a, T: Topology>(score: &'a Box<dyn Score<T>>) -> ListItem<'a> {
|
||||
ListItem::new(score.name())
|
||||
}
|
||||
|
||||
|
@ -4,3 +4,6 @@ edition = "2024"
|
||||
version.workspace = true
|
||||
readme.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1.0.209", features = ["derive"] }
|
||||
|
@ -1,5 +1,7 @@
|
||||
pub mod net {
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize)]
|
||||
pub struct MacAddress(pub [u8; 6]);
|
||||
|
||||
impl MacAddress {
|
||||
|
@ -11,6 +11,7 @@ use crate::{
|
||||
use log::{debug, info, trace, warn};
|
||||
use opnsense_config_xml::OPNsense;
|
||||
use russh::client;
|
||||
use serde::Serialize;
|
||||
|
||||
use super::{ConfigManager, OPNsenseShell};
|
||||
|
||||
@ -21,6 +22,15 @@ pub struct Config {
|
||||
shell: Arc<dyn OPNsenseShell>,
|
||||
}
|
||||
|
||||
impl Serialize for Config {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub async fn new(
|
||||
repository: Arc<dyn ConfigManager>,
|
||||
|
Loading…
Reference in New Issue
Block a user