use serde::{Deserialize, Serialize};
use super::validation::CompatibilityStatus;
use super::validation::VerificationResult;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum AuditEventType {
MigrationStarted,
ValidationCompleted,
SnapshotCaptured,
TransferStarted,
TransferCompleted,
VerificationCompleted,
MigrationCompleted,
MigrationFailed,
RollbackStarted,
RollbackCompleted,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuditEntry {
pub migration_id: [u8; 16],
pub agent_id: [u8; 16],
pub event_type: AuditEventType,
pub timestamp: u64,
pub source_node: Option<[u8; 16]>,
pub target_node: Option<[u8; 16]>,
pub details: String,
pub success: bool,
}
impl AuditEntry {
pub fn new(
migration_id: [u8; 16],
agent_id: [u8; 16],
event_type: AuditEventType,
details: String,
success: bool,
) -> Self {
let timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_secs())
.unwrap_or(0);
Self {
migration_id,
agent_id,
event_type,
timestamp,
source_node: None,
target_node: None,
details,
success,
}
}
pub fn with_nodes(mut self, source: Option<[u8; 16]>, target: Option<[u8; 16]>) -> Self {
self.source_node = source;
self.target_node = target;
self
}
}
#[derive(Debug, Clone, Default)]
pub struct MigrationAuditLog {
entries: Vec<AuditEntry>,
max_entries: usize,
}
impl MigrationAuditLog {
pub fn new() -> Self {
Self {
entries: Vec::new(),
max_entries: 10000,
}
}
pub fn with_capacity(max_entries: usize) -> Self {
Self {
entries: Vec::with_capacity(max_entries.min(1000)),
max_entries,
}
}
pub fn log(&mut self, entry: AuditEntry) {
self.entries.push(entry);
if self.entries.len() > self.max_entries {
let remove_count = self.entries.len() - self.max_entries;
self.entries.drain(0..remove_count);
}
}
pub fn log_start(
&mut self,
migration_id: [u8; 16],
agent_id: [u8; 16],
source: Option<[u8; 16]>,
target: Option<[u8; 16]>,
) {
self.log(
AuditEntry::new(
migration_id,
agent_id,
AuditEventType::MigrationStarted,
"Migration initiated".to_string(),
true,
)
.with_nodes(source, target),
);
}
pub fn log_completion(
&mut self,
migration_id: [u8; 16],
agent_id: [u8; 16],
success: bool,
details: String,
) {
let event_type = if success {
AuditEventType::MigrationCompleted
} else {
AuditEventType::MigrationFailed
};
self.log(AuditEntry::new(
migration_id,
agent_id,
event_type,
details,
success,
));
}
pub fn log_validation(
&mut self,
migration_id: [u8; 16],
agent_id: [u8; 16],
status: &CompatibilityStatus,
) {
let (success, details) = match status {
CompatibilityStatus::Compatible => (true, "Compatibility check passed".to_string()),
CompatibilityStatus::CompatibleWithWarnings(warnings) => (
true,
format!("Compatibility check passed with warnings: {:?}", warnings),
),
CompatibilityStatus::Incompatible(reason) => {
(false, format!("Incompatible: {}", reason))
}
};
self.log(AuditEntry::new(
migration_id,
agent_id,
AuditEventType::ValidationCompleted,
details,
success,
));
}
pub fn log_verification(
&mut self,
migration_id: [u8; 16],
agent_id: [u8; 16],
result: &VerificationResult,
) {
self.log(AuditEntry::new(
migration_id,
agent_id,
AuditEventType::VerificationCompleted,
result.details.join("; "),
result.passed,
));
}
pub fn log_rollback(
&mut self,
migration_id: [u8; 16],
agent_id: [u8; 16],
started: bool,
success: bool,
details: String,
) {
let event_type = if started {
AuditEventType::RollbackStarted
} else {
AuditEventType::RollbackCompleted
};
self.log(AuditEntry::new(
migration_id,
agent_id,
event_type,
details,
success,
));
}
pub fn get_migration_entries(&self, migration_id: &[u8; 16]) -> Vec<&AuditEntry> {
self.entries
.iter()
.filter(|e| &e.migration_id == migration_id)
.collect()
}
pub fn get_agent_entries(&self, agent_id: &[u8; 16]) -> Vec<&AuditEntry> {
self.entries
.iter()
.filter(|e| &e.agent_id == agent_id)
.collect()
}
pub fn get_entries_by_type(&self, event_type: AuditEventType) -> Vec<&AuditEntry> {
self.entries
.iter()
.filter(|e| e.event_type == event_type)
.collect()
}
pub fn get_failures(&self) -> Vec<&AuditEntry> {
self.entries.iter().filter(|e| !e.success).collect()
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn clear(&mut self) {
self.entries.clear();
}
pub fn get_entries_in_range(&self, start: u64, end: u64) -> Vec<&AuditEntry> {
self.entries
.iter()
.filter(|e| e.timestamp >= start && e.timestamp <= end)
.collect()
}
}