kailash 0.9.15__py3-none-any.whl → 0.9.16__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- kailash/middleware/database/base_models.py +7 -1
- kailash/migration/__init__.py +30 -0
- kailash/migration/cli.py +340 -0
- kailash/migration/compatibility_checker.py +662 -0
- kailash/migration/configuration_validator.py +837 -0
- kailash/migration/documentation_generator.py +1828 -0
- kailash/migration/examples/__init__.py +5 -0
- kailash/migration/examples/complete_migration_example.py +692 -0
- kailash/migration/migration_assistant.py +715 -0
- kailash/migration/performance_comparator.py +760 -0
- kailash/migration/regression_detector.py +1141 -0
- kailash/migration/tests/__init__.py +6 -0
- kailash/migration/tests/test_compatibility_checker.py +403 -0
- kailash/migration/tests/test_integration.py +463 -0
- kailash/migration/tests/test_migration_assistant.py +397 -0
- kailash/migration/tests/test_performance_comparator.py +433 -0
- kailash/nodes/data/async_sql.py +1507 -6
- kailash/runtime/local.py +1255 -8
- kailash/runtime/monitoring/__init__.py +1 -0
- kailash/runtime/monitoring/runtime_monitor.py +780 -0
- kailash/runtime/resource_manager.py +3033 -0
- kailash/sdk_exceptions.py +21 -0
- kailash/workflow/cyclic_runner.py +18 -2
- {kailash-0.9.15.dist-info → kailash-0.9.16.dist-info}/METADATA +1 -1
- {kailash-0.9.15.dist-info → kailash-0.9.16.dist-info}/RECORD +30 -12
- {kailash-0.9.15.dist-info → kailash-0.9.16.dist-info}/WHEEL +0 -0
- {kailash-0.9.15.dist-info → kailash-0.9.16.dist-info}/entry_points.txt +0 -0
- {kailash-0.9.15.dist-info → kailash-0.9.16.dist-info}/licenses/LICENSE +0 -0
- {kailash-0.9.15.dist-info → kailash-0.9.16.dist-info}/licenses/NOTICE +0 -0
- {kailash-0.9.15.dist-info → kailash-0.9.16.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,837 @@
|
|
1
|
+
"""Configuration validator for LocalRuntime migration validation.
|
2
|
+
|
3
|
+
This module provides comprehensive validation of LocalRuntime configurations
|
4
|
+
to ensure they are correct, secure, and optimized for the target environment.
|
5
|
+
It validates parameters, detects conflicts, and provides optimization recommendations.
|
6
|
+
"""
|
7
|
+
|
8
|
+
import json
|
9
|
+
import re
|
10
|
+
from dataclasses import dataclass, field
|
11
|
+
from enum import Enum
|
12
|
+
from pathlib import Path
|
13
|
+
from typing import Any, Dict, List, Optional, Set, Tuple, Union
|
14
|
+
|
15
|
+
from kailash.runtime.local import LocalRuntime
|
16
|
+
|
17
|
+
|
18
|
+
class ValidationLevel(Enum):
|
19
|
+
"""Validation severity levels."""
|
20
|
+
|
21
|
+
ERROR = "error" # Configuration is invalid/dangerous
|
22
|
+
WARNING = "warning" # Configuration may cause issues
|
23
|
+
INFO = "info" # Informational/optimization suggestion
|
24
|
+
DEBUG = "debug" # Debug information
|
25
|
+
|
26
|
+
|
27
|
+
class ValidationCategory(Enum):
|
28
|
+
"""Categories of validation checks."""
|
29
|
+
|
30
|
+
SYNTAX = "syntax" # Basic syntax and type validation
|
31
|
+
COMPATIBILITY = "compatibility" # Parameter compatibility
|
32
|
+
SECURITY = "security" # Security-related validation
|
33
|
+
PERFORMANCE = "performance" # Performance optimization
|
34
|
+
RESOURCE = "resource" # Resource management
|
35
|
+
ENTERPRISE = "enterprise" # Enterprise features
|
36
|
+
BEST_PRACTICE = "best_practice" # Best practice recommendations
|
37
|
+
|
38
|
+
|
39
|
+
@dataclass
|
40
|
+
class ValidationIssue:
|
41
|
+
"""Represents a configuration validation issue."""
|
42
|
+
|
43
|
+
level: ValidationLevel
|
44
|
+
category: ValidationCategory
|
45
|
+
parameter: str
|
46
|
+
message: str
|
47
|
+
suggestion: str
|
48
|
+
auto_fixable: bool = False
|
49
|
+
enterprise_feature: bool = False
|
50
|
+
impact: str = "low" # low, medium, high, critical
|
51
|
+
|
52
|
+
|
53
|
+
@dataclass
|
54
|
+
class ValidationResult:
|
55
|
+
"""Results of configuration validation."""
|
56
|
+
|
57
|
+
valid: bool
|
58
|
+
issues: List[ValidationIssue] = field(default_factory=list)
|
59
|
+
optimized_config: Optional[Dict[str, Any]] = None
|
60
|
+
security_score: int = 0 # 0-100
|
61
|
+
performance_score: int = 0 # 0-100
|
62
|
+
enterprise_readiness: int = 0 # 0-100
|
63
|
+
|
64
|
+
|
65
|
+
class ConfigurationValidator:
|
66
|
+
"""Comprehensive LocalRuntime configuration validator."""
|
67
|
+
|
68
|
+
def __init__(self):
|
69
|
+
"""Initialize the configuration validator."""
|
70
|
+
# Valid parameter definitions with types and constraints
|
71
|
+
self.valid_parameters = {
|
72
|
+
"debug": {"type": bool, "default": False},
|
73
|
+
"enable_cycles": {"type": bool, "default": True},
|
74
|
+
"enable_async": {"type": bool, "default": True},
|
75
|
+
"max_concurrency": {"type": int, "min": 1, "max": 1000, "default": 10},
|
76
|
+
"user_context": {"type": object, "optional": True},
|
77
|
+
"enable_monitoring": {"type": bool, "default": True},
|
78
|
+
"enable_security": {"type": bool, "default": False},
|
79
|
+
"enable_audit": {"type": bool, "default": False},
|
80
|
+
"resource_limits": {"type": dict, "optional": True},
|
81
|
+
"secret_provider": {"type": object, "optional": True},
|
82
|
+
"connection_validation": {
|
83
|
+
"type": str,
|
84
|
+
"values": ["strict", "warn", "disable"],
|
85
|
+
"default": "warn",
|
86
|
+
},
|
87
|
+
"conditional_execution": {
|
88
|
+
"type": str,
|
89
|
+
"values": ["route_data", "skip_node"],
|
90
|
+
"default": "route_data",
|
91
|
+
},
|
92
|
+
"content_aware_success_detection": {"type": bool, "default": True},
|
93
|
+
"persistent_mode": {"type": bool, "default": False},
|
94
|
+
"enable_connection_sharing": {"type": bool, "default": True},
|
95
|
+
"max_concurrent_workflows": {
|
96
|
+
"type": int,
|
97
|
+
"min": 1,
|
98
|
+
"max": 100,
|
99
|
+
"default": 10,
|
100
|
+
},
|
101
|
+
"connection_pool_size": {"type": int, "min": 1, "max": 1000, "default": 20},
|
102
|
+
"enable_enterprise_monitoring": {"type": bool, "default": False},
|
103
|
+
"enable_health_monitoring": {"type": bool, "default": False},
|
104
|
+
"enable_resource_coordination": {"type": bool, "default": True},
|
105
|
+
"circuit_breaker_config": {"type": dict, "optional": True},
|
106
|
+
"retry_policy_config": {"type": dict, "optional": True},
|
107
|
+
"connection_pool_config": {"type": dict, "optional": True},
|
108
|
+
}
|
109
|
+
|
110
|
+
# Deprecated parameters
|
111
|
+
self.deprecated_parameters = {
|
112
|
+
"enable_parallel": "Use max_concurrency instead",
|
113
|
+
"thread_pool_size": "Use max_concurrency instead",
|
114
|
+
"memory_limit": "Use resource_limits parameter instead",
|
115
|
+
"timeout": "Use resource_limits parameter instead",
|
116
|
+
"log_level": "Use debug parameter or logging configuration",
|
117
|
+
"cache_enabled": "Use enterprise caching nodes instead",
|
118
|
+
"retry_count": "Use retry_policy_config parameter",
|
119
|
+
}
|
120
|
+
|
121
|
+
# Parameter dependencies
|
122
|
+
self.parameter_dependencies = {
|
123
|
+
"enable_security": ["user_context"],
|
124
|
+
"enable_audit": ["enable_security"],
|
125
|
+
"enable_enterprise_monitoring": ["enable_monitoring"],
|
126
|
+
"persistent_mode": ["enable_connection_sharing"],
|
127
|
+
"circuit_breaker_config": ["enable_resource_coordination"],
|
128
|
+
"retry_policy_config": ["enable_resource_coordination"],
|
129
|
+
}
|
130
|
+
|
131
|
+
# Conflicting parameters
|
132
|
+
self.parameter_conflicts = {
|
133
|
+
("debug", True): [
|
134
|
+
("enable_security", True)
|
135
|
+
], # Debug mode conflicts with security
|
136
|
+
}
|
137
|
+
|
138
|
+
# Security validations
|
139
|
+
self.security_validations = [
|
140
|
+
self._validate_debug_security_conflict,
|
141
|
+
self._validate_audit_requirements,
|
142
|
+
self._validate_user_context_security,
|
143
|
+
self._validate_connection_security,
|
144
|
+
]
|
145
|
+
|
146
|
+
# Performance validations
|
147
|
+
self.performance_validations = [
|
148
|
+
self._validate_concurrency_settings,
|
149
|
+
self._validate_resource_limits,
|
150
|
+
self._validate_connection_pooling,
|
151
|
+
self._validate_monitoring_overhead,
|
152
|
+
]
|
153
|
+
|
154
|
+
# Enterprise validations
|
155
|
+
self.enterprise_validations = [
|
156
|
+
self._validate_enterprise_features,
|
157
|
+
self._validate_monitoring_configuration,
|
158
|
+
self._validate_resilience_configuration,
|
159
|
+
]
|
160
|
+
|
161
|
+
def validate_configuration(self, config: Dict[str, Any]) -> ValidationResult:
|
162
|
+
"""Validate a LocalRuntime configuration.
|
163
|
+
|
164
|
+
Args:
|
165
|
+
config: Configuration dictionary to validate
|
166
|
+
|
167
|
+
Returns:
|
168
|
+
Comprehensive validation results
|
169
|
+
"""
|
170
|
+
result = ValidationResult(valid=True, issues=[])
|
171
|
+
|
172
|
+
# Basic syntax validation
|
173
|
+
self._validate_syntax(config, result)
|
174
|
+
|
175
|
+
# Parameter validation
|
176
|
+
self._validate_parameters(config, result)
|
177
|
+
|
178
|
+
# Dependency validation
|
179
|
+
self._validate_dependencies(config, result)
|
180
|
+
|
181
|
+
# Conflict validation
|
182
|
+
self._validate_conflicts(config, result)
|
183
|
+
|
184
|
+
# Security validation
|
185
|
+
for validator in self.security_validations:
|
186
|
+
validator(config, result)
|
187
|
+
|
188
|
+
# Performance validation
|
189
|
+
for validator in self.performance_validations:
|
190
|
+
validator(config, result)
|
191
|
+
|
192
|
+
# Enterprise validation
|
193
|
+
for validator in self.enterprise_validations:
|
194
|
+
validator(config, result)
|
195
|
+
|
196
|
+
# Generate optimized configuration
|
197
|
+
result.optimized_config = self._generate_optimized_config(config, result)
|
198
|
+
|
199
|
+
# Calculate scores
|
200
|
+
self._calculate_scores(result)
|
201
|
+
|
202
|
+
# Determine overall validity
|
203
|
+
error_issues = [i for i in result.issues if i.level == ValidationLevel.ERROR]
|
204
|
+
result.valid = len(error_issues) == 0
|
205
|
+
|
206
|
+
return result
|
207
|
+
|
208
|
+
def _validate_syntax(
|
209
|
+
self, config: Dict[str, Any], result: ValidationResult
|
210
|
+
) -> None:
|
211
|
+
"""Validate basic syntax and structure."""
|
212
|
+
if not isinstance(config, dict):
|
213
|
+
result.issues.append(
|
214
|
+
ValidationIssue(
|
215
|
+
level=ValidationLevel.ERROR,
|
216
|
+
category=ValidationCategory.SYNTAX,
|
217
|
+
parameter="config",
|
218
|
+
message="Configuration must be a dictionary",
|
219
|
+
suggestion="Ensure configuration is passed as a dictionary",
|
220
|
+
impact="critical",
|
221
|
+
)
|
222
|
+
)
|
223
|
+
return
|
224
|
+
|
225
|
+
# Check for invalid parameter names
|
226
|
+
for param_name in config.keys():
|
227
|
+
if (
|
228
|
+
param_name not in self.valid_parameters
|
229
|
+
and param_name not in self.deprecated_parameters
|
230
|
+
):
|
231
|
+
result.issues.append(
|
232
|
+
ValidationIssue(
|
233
|
+
level=ValidationLevel.WARNING,
|
234
|
+
category=ValidationCategory.SYNTAX,
|
235
|
+
parameter=param_name,
|
236
|
+
message=f"Unknown parameter '{param_name}'",
|
237
|
+
suggestion="Remove unknown parameter or check spelling",
|
238
|
+
impact="medium",
|
239
|
+
)
|
240
|
+
)
|
241
|
+
|
242
|
+
def _validate_parameters(
|
243
|
+
self, config: Dict[str, Any], result: ValidationResult
|
244
|
+
) -> None:
|
245
|
+
"""Validate individual parameters."""
|
246
|
+
for param_name, param_value in config.items():
|
247
|
+
# Check deprecated parameters
|
248
|
+
if param_name in self.deprecated_parameters:
|
249
|
+
result.issues.append(
|
250
|
+
ValidationIssue(
|
251
|
+
level=ValidationLevel.WARNING,
|
252
|
+
category=ValidationCategory.COMPATIBILITY,
|
253
|
+
parameter=param_name,
|
254
|
+
message=f"Parameter '{param_name}' is deprecated",
|
255
|
+
suggestion=self.deprecated_parameters[param_name],
|
256
|
+
auto_fixable=True,
|
257
|
+
impact="medium",
|
258
|
+
)
|
259
|
+
)
|
260
|
+
continue
|
261
|
+
|
262
|
+
# Validate known parameters
|
263
|
+
if param_name in self.valid_parameters:
|
264
|
+
param_def = self.valid_parameters[param_name]
|
265
|
+
|
266
|
+
# Type validation
|
267
|
+
expected_type = param_def["type"]
|
268
|
+
if expected_type != object and not isinstance(
|
269
|
+
param_value, expected_type
|
270
|
+
):
|
271
|
+
result.issues.append(
|
272
|
+
ValidationIssue(
|
273
|
+
level=ValidationLevel.ERROR,
|
274
|
+
category=ValidationCategory.SYNTAX,
|
275
|
+
parameter=param_name,
|
276
|
+
message=f"Parameter '{param_name}' must be of type {expected_type.__name__}, got {type(param_value).__name__}",
|
277
|
+
suggestion=f"Convert value to {expected_type.__name__}",
|
278
|
+
impact="high",
|
279
|
+
)
|
280
|
+
)
|
281
|
+
|
282
|
+
# Range validation for integers
|
283
|
+
if expected_type == int and isinstance(param_value, int):
|
284
|
+
if "min" in param_def and param_value < param_def["min"]:
|
285
|
+
result.issues.append(
|
286
|
+
ValidationIssue(
|
287
|
+
level=ValidationLevel.ERROR,
|
288
|
+
category=ValidationCategory.SYNTAX,
|
289
|
+
parameter=param_name,
|
290
|
+
message=f"Parameter '{param_name}' value {param_value} is below minimum {param_def['min']}",
|
291
|
+
suggestion=f"Set value to at least {param_def['min']}",
|
292
|
+
impact="high",
|
293
|
+
)
|
294
|
+
)
|
295
|
+
|
296
|
+
if "max" in param_def and param_value > param_def["max"]:
|
297
|
+
result.issues.append(
|
298
|
+
ValidationIssue(
|
299
|
+
level=ValidationLevel.WARNING,
|
300
|
+
category=ValidationCategory.PERFORMANCE,
|
301
|
+
parameter=param_name,
|
302
|
+
message=f"Parameter '{param_name}' value {param_value} is above recommended maximum {param_def['max']}",
|
303
|
+
suggestion=f"Consider reducing to {param_def['max']} or below",
|
304
|
+
impact="medium",
|
305
|
+
)
|
306
|
+
)
|
307
|
+
|
308
|
+
# Value validation for strings with restricted values
|
309
|
+
if "values" in param_def and param_value not in param_def["values"]:
|
310
|
+
result.issues.append(
|
311
|
+
ValidationIssue(
|
312
|
+
level=ValidationLevel.ERROR,
|
313
|
+
category=ValidationCategory.SYNTAX,
|
314
|
+
parameter=param_name,
|
315
|
+
message=f"Parameter '{param_name}' value '{param_value}' is not valid. Valid values: {param_def['values']}",
|
316
|
+
suggestion=f"Use one of: {', '.join(param_def['values'])}",
|
317
|
+
impact="high",
|
318
|
+
)
|
319
|
+
)
|
320
|
+
|
321
|
+
def _validate_dependencies(
|
322
|
+
self, config: Dict[str, Any], result: ValidationResult
|
323
|
+
) -> None:
|
324
|
+
"""Validate parameter dependencies."""
|
325
|
+
for param_name, dependencies in self.parameter_dependencies.items():
|
326
|
+
if config.get(param_name):
|
327
|
+
for dep_param in dependencies:
|
328
|
+
if not config.get(dep_param):
|
329
|
+
result.issues.append(
|
330
|
+
ValidationIssue(
|
331
|
+
level=ValidationLevel.WARNING,
|
332
|
+
category=ValidationCategory.COMPATIBILITY,
|
333
|
+
parameter=param_name,
|
334
|
+
message=f"Parameter '{param_name}' requires '{dep_param}' to be set",
|
335
|
+
suggestion=f"Set '{dep_param}' parameter or disable '{param_name}'",
|
336
|
+
impact="medium",
|
337
|
+
)
|
338
|
+
)
|
339
|
+
|
340
|
+
def _validate_conflicts(
|
341
|
+
self, config: Dict[str, Any], result: ValidationResult
|
342
|
+
) -> None:
|
343
|
+
"""Validate parameter conflicts."""
|
344
|
+
for conflict_param, conflict_conditions in self.parameter_conflicts.items():
|
345
|
+
param_name, param_value = conflict_param
|
346
|
+
|
347
|
+
if config.get(param_name) == param_value:
|
348
|
+
for conflict_condition in conflict_conditions:
|
349
|
+
conflict_name, conflict_value = conflict_condition
|
350
|
+
if config.get(conflict_name) == conflict_value:
|
351
|
+
result.issues.append(
|
352
|
+
ValidationIssue(
|
353
|
+
level=ValidationLevel.WARNING,
|
354
|
+
category=ValidationCategory.SECURITY,
|
355
|
+
parameter=param_name,
|
356
|
+
message=f"Parameter '{param_name}={param_value}' conflicts with '{conflict_name}={conflict_value}'",
|
357
|
+
suggestion=f"Disable either '{param_name}' or '{conflict_name}' to resolve conflict",
|
358
|
+
impact="medium",
|
359
|
+
)
|
360
|
+
)
|
361
|
+
|
362
|
+
def _validate_debug_security_conflict(
|
363
|
+
self, config: Dict[str, Any], result: ValidationResult
|
364
|
+
) -> None:
|
365
|
+
"""Validate debug and security configuration conflicts."""
|
366
|
+
if config.get("debug") and config.get("enable_security"):
|
367
|
+
result.issues.append(
|
368
|
+
ValidationIssue(
|
369
|
+
level=ValidationLevel.WARNING,
|
370
|
+
category=ValidationCategory.SECURITY,
|
371
|
+
parameter="debug",
|
372
|
+
message="Debug mode enabled with security features may expose sensitive information",
|
373
|
+
suggestion="Disable debug mode in production or when security is enabled",
|
374
|
+
impact="high",
|
375
|
+
)
|
376
|
+
)
|
377
|
+
|
378
|
+
def _validate_audit_requirements(
|
379
|
+
self, config: Dict[str, Any], result: ValidationResult
|
380
|
+
) -> None:
|
381
|
+
"""Validate audit logging requirements."""
|
382
|
+
if config.get("enable_audit") and not config.get("enable_security"):
|
383
|
+
result.issues.append(
|
384
|
+
ValidationIssue(
|
385
|
+
level=ValidationLevel.ERROR,
|
386
|
+
category=ValidationCategory.SECURITY,
|
387
|
+
parameter="enable_audit",
|
388
|
+
message="Audit logging requires security features to be enabled",
|
389
|
+
suggestion="Enable security with 'enable_security=True'",
|
390
|
+
auto_fixable=True,
|
391
|
+
impact="high",
|
392
|
+
)
|
393
|
+
)
|
394
|
+
|
395
|
+
def _validate_user_context_security(
|
396
|
+
self, config: Dict[str, Any], result: ValidationResult
|
397
|
+
) -> None:
|
398
|
+
"""Validate user context security configuration."""
|
399
|
+
if config.get("user_context") and not config.get("enable_security"):
|
400
|
+
result.issues.append(
|
401
|
+
ValidationIssue(
|
402
|
+
level=ValidationLevel.INFO,
|
403
|
+
category=ValidationCategory.SECURITY,
|
404
|
+
parameter="user_context",
|
405
|
+
message="User context provided but security features are disabled",
|
406
|
+
suggestion="Enable security with 'enable_security=True' to utilize user context",
|
407
|
+
enterprise_feature=True,
|
408
|
+
impact="low",
|
409
|
+
)
|
410
|
+
)
|
411
|
+
|
412
|
+
def _validate_connection_security(
|
413
|
+
self, config: Dict[str, Any], result: ValidationResult
|
414
|
+
) -> None:
|
415
|
+
"""Validate connection security settings."""
|
416
|
+
connection_validation = config.get("connection_validation", "warn")
|
417
|
+
if connection_validation == "disable":
|
418
|
+
result.issues.append(
|
419
|
+
ValidationIssue(
|
420
|
+
level=ValidationLevel.WARNING,
|
421
|
+
category=ValidationCategory.SECURITY,
|
422
|
+
parameter="connection_validation",
|
423
|
+
message="Connection validation is disabled, which may allow invalid connections",
|
424
|
+
suggestion="Use 'warn' or 'strict' for better security",
|
425
|
+
impact="medium",
|
426
|
+
)
|
427
|
+
)
|
428
|
+
|
429
|
+
def _validate_concurrency_settings(
|
430
|
+
self, config: Dict[str, Any], result: ValidationResult
|
431
|
+
) -> None:
|
432
|
+
"""Validate concurrency and performance settings."""
|
433
|
+
max_concurrency = config.get("max_concurrency", 10)
|
434
|
+
max_workflows = config.get("max_concurrent_workflows", 10)
|
435
|
+
pool_size = config.get("connection_pool_size", 20)
|
436
|
+
|
437
|
+
# Check for reasonable concurrency settings
|
438
|
+
if max_concurrency > 50:
|
439
|
+
result.issues.append(
|
440
|
+
ValidationIssue(
|
441
|
+
level=ValidationLevel.WARNING,
|
442
|
+
category=ValidationCategory.PERFORMANCE,
|
443
|
+
parameter="max_concurrency",
|
444
|
+
message=f"High concurrency setting ({max_concurrency}) may cause resource contention",
|
445
|
+
suggestion="Consider reducing concurrency or increasing resource limits",
|
446
|
+
impact="medium",
|
447
|
+
)
|
448
|
+
)
|
449
|
+
|
450
|
+
# Check workflow to concurrency ratio
|
451
|
+
if max_workflows > max_concurrency * 5:
|
452
|
+
result.issues.append(
|
453
|
+
ValidationIssue(
|
454
|
+
level=ValidationLevel.INFO,
|
455
|
+
category=ValidationCategory.PERFORMANCE,
|
456
|
+
parameter="max_concurrent_workflows",
|
457
|
+
message="High workflow concurrency relative to node concurrency",
|
458
|
+
suggestion="Consider increasing max_concurrency or reducing max_concurrent_workflows",
|
459
|
+
impact="low",
|
460
|
+
)
|
461
|
+
)
|
462
|
+
|
463
|
+
# Check connection pool sizing
|
464
|
+
if pool_size < max_concurrency:
|
465
|
+
result.issues.append(
|
466
|
+
ValidationIssue(
|
467
|
+
level=ValidationLevel.WARNING,
|
468
|
+
category=ValidationCategory.PERFORMANCE,
|
469
|
+
parameter="connection_pool_size",
|
470
|
+
message="Connection pool size is smaller than max concurrency",
|
471
|
+
suggestion=f"Increase connection_pool_size to at least {max_concurrency}",
|
472
|
+
auto_fixable=True,
|
473
|
+
impact="medium",
|
474
|
+
)
|
475
|
+
)
|
476
|
+
|
477
|
+
def _validate_resource_limits(
|
478
|
+
self, config: Dict[str, Any], result: ValidationResult
|
479
|
+
) -> None:
|
480
|
+
"""Validate resource limit configurations."""
|
481
|
+
resource_limits = config.get("resource_limits", {})
|
482
|
+
|
483
|
+
if isinstance(resource_limits, dict):
|
484
|
+
# Validate memory limits
|
485
|
+
if "memory_mb" in resource_limits:
|
486
|
+
memory_mb = resource_limits["memory_mb"]
|
487
|
+
if memory_mb < 256:
|
488
|
+
result.issues.append(
|
489
|
+
ValidationIssue(
|
490
|
+
level=ValidationLevel.WARNING,
|
491
|
+
category=ValidationCategory.RESOURCE,
|
492
|
+
parameter="resource_limits.memory_mb",
|
493
|
+
message=f"Low memory limit ({memory_mb}MB) may cause performance issues",
|
494
|
+
suggestion="Consider increasing memory limit to at least 512MB",
|
495
|
+
impact="medium",
|
496
|
+
)
|
497
|
+
)
|
498
|
+
|
499
|
+
# Validate timeout settings
|
500
|
+
if "timeout_seconds" in resource_limits:
|
501
|
+
timeout = resource_limits["timeout_seconds"]
|
502
|
+
if timeout > 3600: # 1 hour
|
503
|
+
result.issues.append(
|
504
|
+
ValidationIssue(
|
505
|
+
level=ValidationLevel.INFO,
|
506
|
+
category=ValidationCategory.RESOURCE,
|
507
|
+
parameter="resource_limits.timeout_seconds",
|
508
|
+
message=f"Very high timeout setting ({timeout}s)",
|
509
|
+
suggestion="Consider if such a long timeout is necessary",
|
510
|
+
impact="low",
|
511
|
+
)
|
512
|
+
)
|
513
|
+
|
514
|
+
def _validate_connection_pooling(
|
515
|
+
self, config: Dict[str, Any], result: ValidationResult
|
516
|
+
) -> None:
|
517
|
+
"""Validate connection pooling configuration."""
|
518
|
+
enable_sharing = config.get("enable_connection_sharing", True)
|
519
|
+
persistent_mode = config.get("persistent_mode", False)
|
520
|
+
|
521
|
+
if persistent_mode and not enable_sharing:
|
522
|
+
result.issues.append(
|
523
|
+
ValidationIssue(
|
524
|
+
level=ValidationLevel.WARNING,
|
525
|
+
category=ValidationCategory.PERFORMANCE,
|
526
|
+
parameter="enable_connection_sharing",
|
527
|
+
message="Persistent mode without connection sharing reduces efficiency",
|
528
|
+
suggestion="Enable connection sharing for better performance in persistent mode",
|
529
|
+
auto_fixable=True,
|
530
|
+
impact="medium",
|
531
|
+
)
|
532
|
+
)
|
533
|
+
|
534
|
+
def _validate_monitoring_overhead(
|
535
|
+
self, config: Dict[str, Any], result: ValidationResult
|
536
|
+
) -> None:
|
537
|
+
"""Validate monitoring configuration for performance impact."""
|
538
|
+
monitoring_features = [
|
539
|
+
"enable_monitoring",
|
540
|
+
"enable_enterprise_monitoring",
|
541
|
+
"enable_health_monitoring",
|
542
|
+
]
|
543
|
+
|
544
|
+
enabled_monitoring = [f for f in monitoring_features if config.get(f)]
|
545
|
+
|
546
|
+
if len(enabled_monitoring) > 2:
|
547
|
+
result.issues.append(
|
548
|
+
ValidationIssue(
|
549
|
+
level=ValidationLevel.INFO,
|
550
|
+
category=ValidationCategory.PERFORMANCE,
|
551
|
+
parameter="monitoring",
|
552
|
+
message="Multiple monitoring features enabled may impact performance",
|
553
|
+
suggestion="Consider enabling only necessary monitoring features for production",
|
554
|
+
impact="low",
|
555
|
+
)
|
556
|
+
)
|
557
|
+
|
558
|
+
def _validate_enterprise_features(
|
559
|
+
self, config: Dict[str, Any], result: ValidationResult
|
560
|
+
) -> None:
|
561
|
+
"""Validate enterprise feature configurations."""
|
562
|
+
enterprise_features = [
|
563
|
+
"enable_security",
|
564
|
+
"enable_audit",
|
565
|
+
"enable_enterprise_monitoring",
|
566
|
+
"enable_health_monitoring",
|
567
|
+
"user_context",
|
568
|
+
]
|
569
|
+
|
570
|
+
enabled_features = [f for f in enterprise_features if config.get(f)]
|
571
|
+
|
572
|
+
if len(enabled_features) > 0:
|
573
|
+
result.issues.append(
|
574
|
+
ValidationIssue(
|
575
|
+
level=ValidationLevel.INFO,
|
576
|
+
category=ValidationCategory.ENTERPRISE,
|
577
|
+
parameter="enterprise_features",
|
578
|
+
message=f"Enterprise features detected: {', '.join(enabled_features)}",
|
579
|
+
suggestion="Ensure enterprise license and proper configuration for production use",
|
580
|
+
enterprise_feature=True,
|
581
|
+
impact="low",
|
582
|
+
)
|
583
|
+
)
|
584
|
+
|
585
|
+
def _validate_monitoring_configuration(
|
586
|
+
self, config: Dict[str, Any], result: ValidationResult
|
587
|
+
) -> None:
|
588
|
+
"""Validate monitoring configuration completeness."""
|
589
|
+
if config.get("enable_enterprise_monitoring") and not config.get(
|
590
|
+
"enable_monitoring"
|
591
|
+
):
|
592
|
+
result.issues.append(
|
593
|
+
ValidationIssue(
|
594
|
+
level=ValidationLevel.ERROR,
|
595
|
+
category=ValidationCategory.ENTERPRISE,
|
596
|
+
parameter="enable_enterprise_monitoring",
|
597
|
+
message="Enterprise monitoring requires basic monitoring to be enabled",
|
598
|
+
suggestion="Enable basic monitoring with 'enable_monitoring=True'",
|
599
|
+
auto_fixable=True,
|
600
|
+
impact="high",
|
601
|
+
)
|
602
|
+
)
|
603
|
+
|
604
|
+
def _validate_resilience_configuration(
|
605
|
+
self, config: Dict[str, Any], result: ValidationResult
|
606
|
+
) -> None:
|
607
|
+
"""Validate resilience and reliability configurations."""
|
608
|
+
circuit_breaker = config.get("circuit_breaker_config")
|
609
|
+
retry_policy = config.get("retry_policy_config")
|
610
|
+
resource_coordination = config.get("enable_resource_coordination", True)
|
611
|
+
|
612
|
+
if (circuit_breaker or retry_policy) and not resource_coordination:
|
613
|
+
result.issues.append(
|
614
|
+
ValidationIssue(
|
615
|
+
level=ValidationLevel.WARNING,
|
616
|
+
category=ValidationCategory.ENTERPRISE,
|
617
|
+
parameter="enable_resource_coordination",
|
618
|
+
message="Resilience features require resource coordination to be enabled",
|
619
|
+
suggestion="Enable resource coordination for circuit breaker and retry policies",
|
620
|
+
auto_fixable=True,
|
621
|
+
enterprise_feature=True,
|
622
|
+
impact="medium",
|
623
|
+
)
|
624
|
+
)
|
625
|
+
|
626
|
+
def _generate_optimized_config(
|
627
|
+
self, config: Dict[str, Any], result: ValidationResult
|
628
|
+
) -> Dict[str, Any]:
|
629
|
+
"""Generate an optimized configuration based on validation results."""
|
630
|
+
optimized = config.copy()
|
631
|
+
|
632
|
+
# Apply auto-fixable improvements
|
633
|
+
for issue in result.issues:
|
634
|
+
if issue.auto_fixable:
|
635
|
+
if issue.parameter == "connection_pool_size":
|
636
|
+
max_concurrency = config.get("max_concurrency", 10)
|
637
|
+
optimized["connection_pool_size"] = max(
|
638
|
+
optimized.get("connection_pool_size", 20), max_concurrency
|
639
|
+
)
|
640
|
+
|
641
|
+
elif issue.parameter == "enable_monitoring" and config.get(
|
642
|
+
"enable_enterprise_monitoring"
|
643
|
+
):
|
644
|
+
optimized["enable_monitoring"] = True
|
645
|
+
|
646
|
+
elif issue.parameter == "enable_security" and config.get(
|
647
|
+
"enable_audit"
|
648
|
+
):
|
649
|
+
optimized["enable_security"] = True
|
650
|
+
|
651
|
+
elif issue.parameter == "enable_connection_sharing" and config.get(
|
652
|
+
"persistent_mode"
|
653
|
+
):
|
654
|
+
optimized["enable_connection_sharing"] = True
|
655
|
+
|
656
|
+
elif issue.parameter == "enable_resource_coordination" and (
|
657
|
+
config.get("circuit_breaker_config")
|
658
|
+
or config.get("retry_policy_config")
|
659
|
+
):
|
660
|
+
optimized["enable_resource_coordination"] = True
|
661
|
+
|
662
|
+
return optimized
|
663
|
+
|
664
|
+
def _calculate_scores(self, result: ValidationResult) -> None:
|
665
|
+
"""Calculate security, performance, and enterprise readiness scores."""
|
666
|
+
total_issues = len(result.issues)
|
667
|
+
error_count = len(
|
668
|
+
[i for i in result.issues if i.level == ValidationLevel.ERROR]
|
669
|
+
)
|
670
|
+
warning_count = len(
|
671
|
+
[i for i in result.issues if i.level == ValidationLevel.WARNING]
|
672
|
+
)
|
673
|
+
|
674
|
+
# Security score (0-100)
|
675
|
+
security_issues = [
|
676
|
+
i for i in result.issues if i.category == ValidationCategory.SECURITY
|
677
|
+
]
|
678
|
+
security_deduction = len(security_issues) * 10
|
679
|
+
result.security_score = max(0, 100 - security_deduction)
|
680
|
+
|
681
|
+
# Performance score (0-100)
|
682
|
+
performance_issues = [
|
683
|
+
i for i in result.issues if i.category == ValidationCategory.PERFORMANCE
|
684
|
+
]
|
685
|
+
performance_deduction = len(performance_issues) * 15
|
686
|
+
result.performance_score = max(0, 100 - performance_deduction)
|
687
|
+
|
688
|
+
# Enterprise readiness score (0-100)
|
689
|
+
enterprise_features = len([i for i in result.issues if i.enterprise_feature])
|
690
|
+
enterprise_issues = [
|
691
|
+
i
|
692
|
+
for i in result.issues
|
693
|
+
if i.category == ValidationCategory.ENTERPRISE
|
694
|
+
and i.level in [ValidationLevel.ERROR, ValidationLevel.WARNING]
|
695
|
+
]
|
696
|
+
|
697
|
+
base_score = 50 if enterprise_features > 0 else 20
|
698
|
+
enterprise_deduction = len(enterprise_issues) * 20
|
699
|
+
result.enterprise_readiness = max(
|
700
|
+
0, base_score + (enterprise_features * 10) - enterprise_deduction
|
701
|
+
)
|
702
|
+
|
703
|
+
def generate_validation_report(
|
704
|
+
self, result: ValidationResult, output_format: str = "text"
|
705
|
+
) -> str:
|
706
|
+
"""Generate a comprehensive validation report.
|
707
|
+
|
708
|
+
Args:
|
709
|
+
result: Validation results
|
710
|
+
output_format: Report format ("text", "json", "markdown")
|
711
|
+
|
712
|
+
Returns:
|
713
|
+
Formatted validation report
|
714
|
+
"""
|
715
|
+
if output_format == "json":
|
716
|
+
return self._generate_json_report(result)
|
717
|
+
elif output_format == "markdown":
|
718
|
+
return self._generate_markdown_report(result)
|
719
|
+
else:
|
720
|
+
return self._generate_text_report(result)
|
721
|
+
|
722
|
+
def _generate_text_report(self, result: ValidationResult) -> str:
|
723
|
+
"""Generate text format validation report."""
|
724
|
+
lines = []
|
725
|
+
lines.append("=" * 60)
|
726
|
+
lines.append("LocalRuntime Configuration Validation Report")
|
727
|
+
lines.append("=" * 60)
|
728
|
+
lines.append("")
|
729
|
+
|
730
|
+
# Summary
|
731
|
+
lines.append("VALIDATION SUMMARY")
|
732
|
+
lines.append("-" * 20)
|
733
|
+
lines.append(f"Configuration Valid: {'Yes' if result.valid else 'No'}")
|
734
|
+
lines.append(f"Total Issues: {len(result.issues)}")
|
735
|
+
lines.append(f"Security Score: {result.security_score}/100")
|
736
|
+
lines.append(f"Performance Score: {result.performance_score}/100")
|
737
|
+
lines.append(f"Enterprise Readiness: {result.enterprise_readiness}/100")
|
738
|
+
lines.append("")
|
739
|
+
|
740
|
+
# Issues by level
|
741
|
+
for level in ValidationLevel:
|
742
|
+
level_issues = [i for i in result.issues if i.level == level]
|
743
|
+
if level_issues:
|
744
|
+
lines.append(f"{level.value.upper()} ISSUES ({len(level_issues)})")
|
745
|
+
lines.append("-" * (len(level.value) + 15))
|
746
|
+
|
747
|
+
for issue in level_issues:
|
748
|
+
lines.append(f"• {issue.message}")
|
749
|
+
lines.append(f" Parameter: {issue.parameter}")
|
750
|
+
lines.append(f" Category: {issue.category.value}")
|
751
|
+
lines.append(f" Suggestion: {issue.suggestion}")
|
752
|
+
if issue.auto_fixable:
|
753
|
+
lines.append(" Auto-fixable: Yes")
|
754
|
+
lines.append("")
|
755
|
+
|
756
|
+
return "\n".join(lines)
|
757
|
+
|
758
|
+
def _generate_json_report(self, result: ValidationResult) -> str:
|
759
|
+
"""Generate JSON format validation report."""
|
760
|
+
data = {
|
761
|
+
"valid": result.valid,
|
762
|
+
"scores": {
|
763
|
+
"security": result.security_score,
|
764
|
+
"performance": result.performance_score,
|
765
|
+
"enterprise_readiness": result.enterprise_readiness,
|
766
|
+
},
|
767
|
+
"issues": [
|
768
|
+
{
|
769
|
+
"level": issue.level.value,
|
770
|
+
"category": issue.category.value,
|
771
|
+
"parameter": issue.parameter,
|
772
|
+
"message": issue.message,
|
773
|
+
"suggestion": issue.suggestion,
|
774
|
+
"auto_fixable": issue.auto_fixable,
|
775
|
+
"enterprise_feature": issue.enterprise_feature,
|
776
|
+
"impact": issue.impact,
|
777
|
+
}
|
778
|
+
for issue in result.issues
|
779
|
+
],
|
780
|
+
"optimized_config": result.optimized_config,
|
781
|
+
}
|
782
|
+
|
783
|
+
return json.dumps(data, indent=2)
|
784
|
+
|
785
|
+
def _generate_markdown_report(self, result: ValidationResult) -> str:
|
786
|
+
"""Generate markdown format validation report."""
|
787
|
+
lines = []
|
788
|
+
lines.append("# LocalRuntime Configuration Validation Report")
|
789
|
+
lines.append("")
|
790
|
+
|
791
|
+
# Summary
|
792
|
+
lines.append("## Summary")
|
793
|
+
lines.append("")
|
794
|
+
lines.append("| Metric | Score |")
|
795
|
+
lines.append("|--------|-------|")
|
796
|
+
lines.append(
|
797
|
+
f"| Configuration Valid | {'✅ Yes' if result.valid else '❌ No'} |"
|
798
|
+
)
|
799
|
+
lines.append(f"| Total Issues | {len(result.issues)} |")
|
800
|
+
lines.append(f"| Security Score | {result.security_score}/100 |")
|
801
|
+
lines.append(f"| Performance Score | {result.performance_score}/100 |")
|
802
|
+
lines.append(f"| Enterprise Readiness | {result.enterprise_readiness}/100 |")
|
803
|
+
lines.append("")
|
804
|
+
|
805
|
+
# Issues
|
806
|
+
if result.issues:
|
807
|
+
lines.append("## Issues")
|
808
|
+
lines.append("")
|
809
|
+
|
810
|
+
for level in ValidationLevel:
|
811
|
+
level_issues = [i for i in result.issues if i.level == level]
|
812
|
+
if level_issues:
|
813
|
+
level_emoji = {
|
814
|
+
ValidationLevel.ERROR: "🚨",
|
815
|
+
ValidationLevel.WARNING: "⚠️",
|
816
|
+
ValidationLevel.INFO: "ℹ️",
|
817
|
+
ValidationLevel.DEBUG: "🔍",
|
818
|
+
}
|
819
|
+
|
820
|
+
lines.append(
|
821
|
+
f"### {level_emoji.get(level, '')} {level.value.title()} Issues"
|
822
|
+
)
|
823
|
+
lines.append("")
|
824
|
+
|
825
|
+
for issue in level_issues:
|
826
|
+
lines.append(f"**{issue.parameter}**: {issue.message}")
|
827
|
+
lines.append("")
|
828
|
+
lines.append(f"- **Suggestion**: {issue.suggestion}")
|
829
|
+
lines.append(f"- **Category**: {issue.category.value}")
|
830
|
+
lines.append(f"- **Impact**: {issue.impact}")
|
831
|
+
if issue.auto_fixable:
|
832
|
+
lines.append("- **Auto-fixable**: ✅ Yes")
|
833
|
+
if issue.enterprise_feature:
|
834
|
+
lines.append("- **Enterprise Feature**: 🏢 Yes")
|
835
|
+
lines.append("")
|
836
|
+
|
837
|
+
return "\n".join(lines)
|