1use super::monitoring::AlertSeverity;
7use super::resource_types::{
8 AllocationPriority, ResourceAllocation, ResourceUsage, SecurityConstraints,
9};
10use crate::execution_config::ResourceConstraints;
11use crate::task_definitions::TaskRequirements;
12use sklears_core::error::{Result as SklResult, SklearsError};
13use std::collections::HashMap;
14use std::time::{Duration, SystemTime};
15
16#[derive(Debug)]
18pub struct ResourceConstraintChecker {
19 global_constraints: ResourceConstraints,
21 policy_rules: Vec<PolicyRule>,
23 validation_cache: HashMap<String, ValidationResult>,
25 config: ConstraintCheckerConfig,
27 stats: ValidationStats,
29}
30
31#[derive(Debug, Clone)]
33pub struct ConstraintCheckerConfig {
34 pub enable_caching: bool,
36 pub cache_ttl: Duration,
38 pub strict_mode: bool,
40 pub enable_security_validation: bool,
42 pub enable_performance_validation: bool,
44 pub validation_timeout: Duration,
46}
47
48#[derive(Debug, Clone)]
50pub struct PolicyRule {
51 pub id: String,
53 pub name: String,
55 pub description: String,
57 pub condition: PolicyCondition,
59 pub action: PolicyAction,
61 pub priority: u32,
63 pub enabled: bool,
65 pub metadata: HashMap<String, String>,
67}
68
69#[derive(Debug, Clone)]
71pub enum PolicyCondition {
72 ResourceThreshold {
74 resource_type: String,
75 threshold: f64,
76 operator: ComparisonOperator,
77 },
78 UserConstraint {
80 users: Vec<String>,
81 groups: Vec<String>,
82 operation: SetOperation,
83 },
84 TimeConstraint {
86 start_time: u32, end_time: u32, days_of_week: Vec<u8>, },
90 SecurityConstraint {
92 security_level: SecurityLevel,
93 isolation_required: bool,
94 encryption_required: bool,
95 },
96 PerformanceConstraint {
98 min_performance: f64,
99 max_latency: Duration,
100 min_throughput: f64,
101 },
102 Custom {
104 expression: String,
105 parameters: HashMap<String, String>,
106 },
107 Composite {
109 operator: LogicalOperator,
110 conditions: Vec<PolicyCondition>,
111 },
112}
113
114#[derive(Debug, Clone, PartialEq)]
116pub enum ComparisonOperator {
117 Equal,
119 NotEqual,
121 GreaterThan,
123 GreaterThanOrEqual,
125 LessThan,
127 LessThanOrEqual,
129}
130
131#[derive(Debug, Clone, PartialEq)]
133pub enum SetOperation {
134 Include,
136 Exclude,
138 Any,
140 All,
142}
143
144#[derive(Debug, Clone, PartialEq, PartialOrd)]
146pub enum SecurityLevel {
147 Public,
149 Internal,
151 Confidential,
153 Secret,
155 TopSecret,
157}
158
159#[derive(Debug, Clone, PartialEq)]
161pub enum LogicalOperator {
162 And,
164 Or,
166 Not,
168}
169
170#[derive(Debug, Clone)]
172pub enum PolicyAction {
173 Allow,
175 Deny { reason: String },
177 Modify {
179 modifications: AllocationModifications,
180 },
181 RequireApproval {
183 approvers: Vec<String>,
184 timeout: Duration,
185 },
186 Log { level: LogLevel, message: String },
188 Alert {
190 severity: AlertSeverity,
191 message: String,
192 channels: Vec<String>,
193 },
194 RateLimit {
196 max_allocations: u32,
197 time_window: Duration,
198 },
199}
200
201#[derive(Debug, Clone)]
203pub struct AllocationModifications {
204 pub reduce_cpu: Option<f64>,
206 pub reduce_memory: Option<f64>,
208 pub new_priority: Option<AllocationPriority>,
210 pub add_security_constraints: Option<SecurityConstraints>,
212 pub time_limit: Option<Duration>,
214}
215
216#[derive(Debug, Clone, PartialEq)]
218pub enum LogLevel {
219 Debug,
221 Info,
223 Warning,
225 Error,
227 Critical,
229}
230
231#[derive(Debug, Clone)]
233pub struct ValidationResult {
234 pub valid: bool,
236 pub errors: Vec<ValidationError>,
238 pub warnings: Vec<ValidationWarning>,
240 pub applied_actions: Vec<PolicyAction>,
242 pub timestamp: SystemTime,
244 pub duration: Duration,
246 pub modified_allocation: Option<ResourceAllocation>,
248}
249
250#[derive(Debug, Clone)]
252pub struct ValidationError {
253 pub code: String,
255 pub message: String,
257 pub policy_rule_id: Option<String>,
259 pub severity: ErrorSeverity,
261 pub context: HashMap<String, String>,
263}
264
265#[derive(Debug, Clone, PartialEq, PartialOrd)]
267pub enum ErrorSeverity {
268 Low,
270 Medium,
272 High,
274 Critical,
276}
277
278#[derive(Debug, Clone)]
280pub struct ValidationWarning {
281 pub code: String,
283 pub message: String,
285 pub policy_rule_id: Option<String>,
287 pub context: HashMap<String, String>,
289}
290
291#[derive(Debug, Clone)]
293pub struct ValidationStats {
294 pub total_validations: u64,
296 pub successful_validations: u64,
298 pub failed_validations: u64,
300 pub avg_validation_time: Duration,
302 pub policy_rule_hits: HashMap<String, u64>,
304 pub cache_hit_rate: f64,
306}
307
308#[derive(Debug, Clone)]
310pub struct ValidationContext {
311 pub user: Option<String>,
313 pub groups: Vec<String>,
315 pub current_time: SystemTime,
317 pub current_usage: ResourceUsage,
319 pub available_resources: ResourceUsage,
321 pub system_load: f64,
323 pub security_context: SecurityContext,
325}
326
327#[derive(Debug, Clone)]
329pub struct SecurityContext {
330 pub level: SecurityLevel,
332 pub clearance: Option<String>,
334 pub tokens: Vec<String>,
336 pub tee_available: bool,
338 pub encryption_available: bool,
340}
341
342impl Default for ResourceConstraintChecker {
343 fn default() -> Self {
344 Self::new()
345 }
346}
347
348impl ResourceConstraintChecker {
349 #[must_use]
351 pub fn new() -> Self {
352 Self {
353 global_constraints: ResourceConstraints::default(),
354 policy_rules: Vec::new(),
355 validation_cache: HashMap::new(),
356 config: ConstraintCheckerConfig::default(),
357 stats: ValidationStats::default(),
358 }
359 }
360
361 pub fn set_global_constraints(&mut self, constraints: ResourceConstraints) {
363 self.global_constraints = constraints;
364 }
365
366 pub fn add_policy_rule(&mut self, rule: PolicyRule) {
368 self.policy_rules.push(rule);
369 self.policy_rules
371 .sort_by(|a, b| b.priority.cmp(&a.priority));
372 }
373
374 pub fn remove_policy_rule(&mut self, rule_id: &str) -> bool {
376 if let Some(pos) = self.policy_rules.iter().position(|r| r.id == rule_id) {
377 self.policy_rules.remove(pos);
378 true
379 } else {
380 false
381 }
382 }
383
384 pub fn validate_allocation(
386 &mut self,
387 requirements: &TaskRequirements,
388 context: &ValidationContext,
389 ) -> SklResult<ValidationResult> {
390 let start_time = SystemTime::now();
391
392 if self.config.enable_caching {
394 let cache_key = self.generate_cache_key(requirements, context);
395 if let Some(cached_result) = self.validation_cache.get(&cache_key) {
396 if start_time
397 .duration_since(cached_result.timestamp)
398 .unwrap_or(Duration::MAX)
399 < self.config.cache_ttl
400 {
401 return Ok(cached_result.clone());
402 }
403 }
404 }
405
406 let mut result = ValidationResult {
407 valid: true,
408 errors: Vec::new(),
409 warnings: Vec::new(),
410 applied_actions: Vec::new(),
411 timestamp: start_time,
412 duration: Duration::from_secs(0),
413 modified_allocation: None,
414 };
415
416 self.validate_global_constraints(requirements, context, &mut result)?;
418
419 self.apply_policy_rules(requirements, context, &mut result)?;
421
422 if self.config.enable_security_validation {
424 self.validate_security_constraints(requirements, context, &mut result)?;
425 }
426
427 if self.config.enable_performance_validation {
429 self.validate_performance_constraints(requirements, context, &mut result)?;
430 }
431
432 result.duration = SystemTime::now()
434 .duration_since(start_time)
435 .unwrap_or(Duration::from_secs(0));
436
437 self.update_stats(&result);
439
440 if self.config.enable_caching {
442 let cache_key = self.generate_cache_key(requirements, context);
443 self.validation_cache.insert(cache_key, result.clone());
444 }
445
446 Ok(result)
447 }
448
449 fn validate_global_constraints(
451 &self,
452 requirements: &TaskRequirements,
453 context: &ValidationContext,
454 result: &mut ValidationResult,
455 ) -> SklResult<()> {
456 if let Some(cpu_cores) = requirements.cpu_cores {
458 if let Some(max_cpu) = self.global_constraints.max_cpu_cores {
459 if cpu_cores > max_cpu {
460 result.valid = false;
461 result.errors.push(ValidationError {
462 code: "CPU_LIMIT_EXCEEDED".to_string(),
463 message: format!(
464 "Requested {cpu_cores} CPU cores exceeds limit of {max_cpu}"
465 ),
466 policy_rule_id: None,
467 severity: ErrorSeverity::High,
468 context: HashMap::new(),
469 });
470 }
471 }
472 }
473
474 if let Some(memory) = requirements.memory {
476 if let Some(max_memory) = self.global_constraints.max_memory {
477 if memory > max_memory {
478 result.valid = false;
479 result.errors.push(ValidationError {
480 code: "MEMORY_LIMIT_EXCEEDED".to_string(),
481 message: format!(
482 "Requested {memory} bytes memory exceeds limit of {max_memory}"
483 ),
484 policy_rule_id: None,
485 severity: ErrorSeverity::High,
486 context: HashMap::new(),
487 });
488 }
489 }
490 }
491
492 if !requirements.gpu_devices.is_empty() {
494 if let Some(max_gpus) = self.global_constraints.max_gpu_devices {
495 if requirements.gpu_devices.len() > max_gpus {
496 result.valid = false;
497 result.errors.push(ValidationError {
498 code: "GPU_LIMIT_EXCEEDED".to_string(),
499 message: format!(
500 "Requested {} GPU devices exceeds limit of {}",
501 requirements.gpu_devices.len(),
502 max_gpus
503 ),
504 policy_rule_id: None,
505 severity: ErrorSeverity::High,
506 context: HashMap::new(),
507 });
508 }
509 }
510 }
511
512 Ok(())
513 }
514
515 fn apply_policy_rules(
517 &mut self,
518 requirements: &TaskRequirements,
519 context: &ValidationContext,
520 result: &mut ValidationResult,
521 ) -> SklResult<()> {
522 for rule in &self.policy_rules {
523 if !rule.enabled {
524 continue;
525 }
526
527 if self.evaluate_condition(&rule.condition, requirements, context)? {
529 *self
531 .stats
532 .policy_rule_hits
533 .entry(rule.id.clone())
534 .or_insert(0) += 1;
535
536 match &rule.action {
538 PolicyAction::Allow => {
539 }
541 PolicyAction::Deny { reason } => {
542 result.valid = false;
543 result.errors.push(ValidationError {
544 code: "POLICY_DENIED".to_string(),
545 message: reason.clone(),
546 policy_rule_id: Some(rule.id.clone()),
547 severity: ErrorSeverity::High,
548 context: HashMap::new(),
549 });
550 }
551 PolicyAction::Modify { modifications } => {
552 result.warnings.push(ValidationWarning {
554 code: "ALLOCATION_MODIFIED".to_string(),
555 message: "Allocation was modified by policy rule".to_string(),
556 policy_rule_id: Some(rule.id.clone()),
557 context: HashMap::new(),
558 });
559 }
560 PolicyAction::RequireApproval {
561 approvers: _,
562 timeout: _,
563 } => {
564 result.warnings.push(ValidationWarning {
565 code: "APPROVAL_REQUIRED".to_string(),
566 message: "Allocation requires approval".to_string(),
567 policy_rule_id: Some(rule.id.clone()),
568 context: HashMap::new(),
569 });
570 }
571 PolicyAction::Log { level: _, message } => {
572 result.warnings.push(ValidationWarning {
574 code: "LOGGED_ALLOCATION".to_string(),
575 message: message.clone(),
576 policy_rule_id: Some(rule.id.clone()),
577 context: HashMap::new(),
578 });
579 }
580 PolicyAction::Alert {
581 severity: _,
582 message,
583 channels: _,
584 } => {
585 result.warnings.push(ValidationWarning {
586 code: "ALERT_TRIGGERED".to_string(),
587 message: message.clone(),
588 policy_rule_id: Some(rule.id.clone()),
589 context: HashMap::new(),
590 });
591 }
592 PolicyAction::RateLimit {
593 max_allocations: _,
594 time_window: _,
595 } => {
596 result.warnings.push(ValidationWarning {
598 code: "RATE_LIMITED".to_string(),
599 message: "Allocation is subject to rate limiting".to_string(),
600 policy_rule_id: Some(rule.id.clone()),
601 context: HashMap::new(),
602 });
603 }
604 }
605
606 result.applied_actions.push(rule.action.clone());
607 }
608 }
609
610 Ok(())
611 }
612
613 fn evaluate_condition(
615 &self,
616 condition: &PolicyCondition,
617 requirements: &TaskRequirements,
618 context: &ValidationContext,
619 ) -> SklResult<bool> {
620 match condition {
621 PolicyCondition::ResourceThreshold {
622 resource_type,
623 threshold,
624 operator,
625 } => {
626 let current_usage = match resource_type.as_str() {
627 "cpu" => context.current_usage.cpu_percent,
628 "memory" => {
629 (context.current_usage.memory_usage.used as f64
630 / context.current_usage.memory_usage.total as f64)
631 * 100.0
632 }
633 _ => return Ok(false),
634 };
635
636 Ok(self.compare_values(current_usage, *threshold, operator))
637 }
638 PolicyCondition::UserConstraint {
639 users,
640 groups,
641 operation,
642 } => {
643 if let Some(user) = &context.user {
644 let user_match = users.contains(user);
645 let group_match = groups.iter().any(|g| context.groups.contains(g));
646
647 Ok(match operation {
648 SetOperation::Include => user_match || group_match,
649 SetOperation::Exclude => !user_match && !group_match,
650 SetOperation::Any => user_match || group_match,
651 SetOperation::All => {
652 user_match && groups.iter().all(|g| context.groups.contains(g))
653 }
654 })
655 } else {
656 Ok(false)
657 }
658 }
659 PolicyCondition::TimeConstraint {
660 start_time,
661 end_time,
662 days_of_week,
663 } => {
664 let now = context
666 .current_time
667 .duration_since(std::time::UNIX_EPOCH)
668 .unwrap_or(Duration::from_secs(0));
669 let seconds_today = (now.as_secs() % (24 * 3600)) as u32;
670 let day_of_week = ((now.as_secs() / (24 * 3600)) % 7) as u8; let time_match = if start_time <= end_time {
673 seconds_today >= *start_time && seconds_today <= *end_time
674 } else {
675 seconds_today >= *start_time || seconds_today <= *end_time
676 };
677
678 let day_match = days_of_week.is_empty() || days_of_week.contains(&day_of_week);
679
680 Ok(time_match && day_match)
681 }
682 PolicyCondition::SecurityConstraint {
683 security_level,
684 isolation_required,
685 encryption_required,
686 } => {
687 let level_match = context.security_context.level >= *security_level;
688 let isolation_match = !isolation_required || context.security_context.tee_available;
689 let encryption_match =
690 !encryption_required || context.security_context.encryption_available;
691
692 Ok(level_match && isolation_match && encryption_match)
693 }
694 PolicyCondition::PerformanceConstraint {
695 min_performance: _,
696 max_latency: _,
697 min_throughput: _,
698 } => {
699 Ok(true) }
702 PolicyCondition::Custom {
703 expression: _,
704 parameters: _,
705 } => {
706 Ok(false) }
709 PolicyCondition::Composite {
710 operator,
711 conditions,
712 } => match operator {
713 LogicalOperator::And => {
714 for cond in conditions {
715 if !self.evaluate_condition(cond, requirements, context)? {
716 return Ok(false);
717 }
718 }
719 Ok(true)
720 }
721 LogicalOperator::Or => {
722 for cond in conditions {
723 if self.evaluate_condition(cond, requirements, context)? {
724 return Ok(true);
725 }
726 }
727 Ok(false)
728 }
729 LogicalOperator::Not => {
730 if conditions.len() != 1 {
731 return Err(SklearsError::ResourceAllocationError(
732 "NOT operator requires exactly one condition".to_string(),
733 ));
734 }
735 Ok(!self.evaluate_condition(&conditions[0], requirements, context)?)
736 }
737 },
738 }
739 }
740
741 fn compare_values(&self, left: f64, right: f64, operator: &ComparisonOperator) -> bool {
743 match operator {
744 ComparisonOperator::Equal => (left - right).abs() < f64::EPSILON,
745 ComparisonOperator::NotEqual => (left - right).abs() >= f64::EPSILON,
746 ComparisonOperator::GreaterThan => left > right,
747 ComparisonOperator::GreaterThanOrEqual => left >= right,
748 ComparisonOperator::LessThan => left < right,
749 ComparisonOperator::LessThanOrEqual => left <= right,
750 }
751 }
752
753 fn validate_security_constraints(
755 &self,
756 requirements: &TaskRequirements,
757 context: &ValidationContext,
758 result: &mut ValidationResult,
759 ) -> SklResult<()> {
760 Ok(())
779 }
780
781 fn validate_performance_constraints(
783 &self,
784 _requirements: &TaskRequirements,
785 context: &ValidationContext,
786 result: &mut ValidationResult,
787 ) -> SklResult<()> {
788 if context.system_load > 0.9 {
790 result.warnings.push(ValidationWarning {
791 code: "HIGH_SYSTEM_LOAD".to_string(),
792 message: "System load is high, performance may be degraded".to_string(),
793 policy_rule_id: None,
794 context: HashMap::new(),
795 });
796 }
797
798 Ok(())
799 }
800
801 fn generate_cache_key(
803 &self,
804 requirements: &TaskRequirements,
805 context: &ValidationContext,
806 ) -> String {
807 use std::collections::hash_map::DefaultHasher;
808 use std::hash::{Hash, Hasher};
809
810 let mut hasher = DefaultHasher::new();
811 requirements.cpu_cores.hash(&mut hasher);
812 requirements.memory.hash(&mut hasher);
813 requirements.gpu_devices.hash(&mut hasher);
814 context.user.hash(&mut hasher);
815 context.groups.hash(&mut hasher);
816
817 format!("validation_{}", hasher.finish())
818 }
819
820 fn update_stats(&mut self, result: &ValidationResult) {
822 self.stats.total_validations += 1;
823 if result.valid {
824 self.stats.successful_validations += 1;
825 } else {
826 self.stats.failed_validations += 1;
827 }
828
829 let total_time = self.stats.avg_validation_time.as_nanos() as f64
831 * (self.stats.total_validations - 1) as f64
832 + result.duration.as_nanos() as f64;
833 self.stats.avg_validation_time =
834 Duration::from_nanos((total_time / self.stats.total_validations as f64) as u64);
835 }
836
837 #[must_use]
839 pub fn get_stats(&self) -> &ValidationStats {
840 &self.stats
841 }
842}
843
844impl Default for ConstraintCheckerConfig {
845 fn default() -> Self {
846 Self {
847 enable_caching: true,
848 cache_ttl: Duration::from_secs(300), strict_mode: false,
850 enable_security_validation: true,
851 enable_performance_validation: true,
852 validation_timeout: Duration::from_secs(10),
853 }
854 }
855}
856
857impl Default for ValidationStats {
858 fn default() -> Self {
859 Self {
860 total_validations: 0,
861 successful_validations: 0,
862 failed_validations: 0,
863 avg_validation_time: Duration::from_millis(0),
864 policy_rule_hits: HashMap::new(),
865 cache_hit_rate: 0.0,
866 }
867 }
868}