runbooks 1.1.5__py3-none-any.whl → 1.1.7__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.
- runbooks/__init__.py +1 -1
- runbooks/cli/commands/finops.py +29 -5
- runbooks/cli/commands/inventory.py +40 -87
- runbooks/common/accuracy_validator.py +6 -12
- runbooks/common/cli_decorators.py +61 -0
- runbooks/common/mcp_integration.py +38 -7
- runbooks/common/rich_utils.py +116 -2
- runbooks/inventory/CLAUDE.md +1 -1
- runbooks/inventory/aws_decorators.py +2 -3
- runbooks/inventory/check_cloudtrail_compliance.py +2 -4
- runbooks/inventory/check_controltower_readiness.py +152 -151
- runbooks/inventory/check_landingzone_readiness.py +85 -84
- runbooks/inventory/core/formatter.py +11 -0
- runbooks/inventory/draw_org_structure.py +8 -9
- runbooks/inventory/ec2_vpc_utils.py +2 -2
- runbooks/inventory/find_cfn_drift_detection.py +5 -7
- runbooks/inventory/find_cfn_orphaned_stacks.py +7 -9
- runbooks/inventory/find_cfn_stackset_drift.py +5 -6
- runbooks/inventory/find_ec2_security_groups.py +48 -42
- runbooks/inventory/find_landingzone_versions.py +4 -6
- runbooks/inventory/find_vpc_flow_logs.py +7 -9
- runbooks/inventory/inventory_modules.py +103 -91
- runbooks/inventory/list_cfn_stacks.py +9 -10
- runbooks/inventory/list_cfn_stackset_operation_results.py +1 -3
- runbooks/inventory/list_cfn_stackset_operations.py +79 -57
- runbooks/inventory/list_cfn_stacksets.py +8 -10
- runbooks/inventory/list_config_recorders_delivery_channels.py +49 -39
- runbooks/inventory/list_ds_directories.py +65 -53
- runbooks/inventory/list_ec2_availability_zones.py +2 -4
- runbooks/inventory/list_ec2_ebs_volumes.py +32 -35
- runbooks/inventory/list_ec2_instances.py +23 -28
- runbooks/inventory/list_ecs_clusters_and_tasks.py +26 -34
- runbooks/inventory/list_elbs_load_balancers.py +22 -20
- runbooks/inventory/list_enis_network_interfaces.py +26 -33
- runbooks/inventory/list_guardduty_detectors.py +2 -4
- runbooks/inventory/list_iam_policies.py +2 -4
- runbooks/inventory/list_iam_roles.py +5 -7
- runbooks/inventory/list_iam_saml_providers.py +4 -6
- runbooks/inventory/list_lambda_functions.py +38 -38
- runbooks/inventory/list_org_accounts.py +6 -8
- runbooks/inventory/list_org_accounts_users.py +55 -44
- runbooks/inventory/list_rds_db_instances.py +31 -33
- runbooks/inventory/list_route53_hosted_zones.py +3 -5
- runbooks/inventory/list_servicecatalog_provisioned_products.py +37 -41
- runbooks/inventory/list_sns_topics.py +2 -4
- runbooks/inventory/list_ssm_parameters.py +4 -7
- runbooks/inventory/list_vpc_subnets.py +2 -4
- runbooks/inventory/list_vpcs.py +7 -10
- runbooks/inventory/mcp_inventory_validator.py +5 -3
- runbooks/inventory/organizations_discovery.py +8 -4
- runbooks/inventory/recover_cfn_stack_ids.py +7 -8
- runbooks/inventory/requirements.txt +0 -1
- runbooks/inventory/rich_inventory_display.py +2 -2
- runbooks/inventory/run_on_multi_accounts.py +3 -5
- runbooks/inventory/unified_validation_engine.py +3 -2
- runbooks/inventory/verify_ec2_security_groups.py +1 -1
- runbooks/inventory/vpc_analyzer.py +3 -2
- runbooks/inventory/vpc_dependency_analyzer.py +2 -2
- runbooks/validation/terraform_drift_detector.py +16 -5
- {runbooks-1.1.5.dist-info → runbooks-1.1.7.dist-info}/METADATA +3 -4
- {runbooks-1.1.5.dist-info → runbooks-1.1.7.dist-info}/RECORD +65 -65
- {runbooks-1.1.5.dist-info → runbooks-1.1.7.dist-info}/WHEEL +0 -0
- {runbooks-1.1.5.dist-info → runbooks-1.1.7.dist-info}/entry_points.txt +0 -0
- {runbooks-1.1.5.dist-info → runbooks-1.1.7.dist-info}/licenses/LICENSE +0 -0
- {runbooks-1.1.5.dist-info → runbooks-1.1.7.dist-info}/top_level.txt +0 -0
@@ -64,14 +64,11 @@ from time import time
|
|
64
64
|
|
65
65
|
from ArgumentsClass import CommonArguments
|
66
66
|
from botocore.exceptions import ClientError
|
67
|
-
from
|
67
|
+
from runbooks.common.rich_utils import console
|
68
68
|
from Inventory_Modules import display_results, find_ssm_parameters2, get_all_credentials
|
69
|
-
from tqdm.auto import tqdm
|
70
69
|
|
71
|
-
init()
|
72
70
|
__version__ = "2024.05.07"
|
73
71
|
begin_time = time()
|
74
|
-
ERASE_LINE = "\x1b[2K"
|
75
72
|
|
76
73
|
|
77
74
|
##################
|
@@ -236,7 +233,7 @@ def find_ssm_parameters(f_credentialList):
|
|
236
233
|
parameter_list.extend(find_ssm_parameters2(credential))
|
237
234
|
# Optional verbose logging for parameter discovery progress (currently commented)
|
238
235
|
# if verbose < 50 or len(parameter_list) == 0:
|
239
|
-
# print(f"Found a running total of {len(parameter_list)} parameters in account {
|
236
|
+
# print(f"Found a running total of {len(parameter_list)} parameters in account [red]{credential['AccountNumber']} in region [red]{credential['Region']}")
|
240
237
|
except ClientError as my_Error:
|
241
238
|
# Handle AWS API authorization failures gracefully
|
242
239
|
if "AuthFailure" in str(my_Error):
|
@@ -404,7 +401,7 @@ if __name__ == "__main__":
|
|
404
401
|
# Display performance timing metrics for operational optimization
|
405
402
|
if pTiming:
|
406
403
|
print(ERASE_LINE)
|
407
|
-
print(f"
|
404
|
+
print(f"[green]This script took {time() - begin_time:.2f} seconds")
|
408
405
|
|
409
406
|
print()
|
410
407
|
|
@@ -424,7 +421,7 @@ if __name__ == "__main__":
|
|
424
421
|
# Display completion message and output file information
|
425
422
|
print("Thank you for using this script")
|
426
423
|
print(
|
427
|
-
f"Your output was saved to
|
424
|
+
f"Your output was saved to [green]'{pFilename}-{datetime.now().strftime('%y-%m-%d--%H:%M:%S')}'"
|
428
425
|
) if pFilename is not None else None
|
429
426
|
|
430
427
|
print()
|
@@ -76,10 +76,9 @@ from time import time
|
|
76
76
|
import Inventory_Modules
|
77
77
|
from ArgumentsClass import CommonArguments
|
78
78
|
from botocore.exceptions import ClientError
|
79
|
-
from
|
79
|
+
from runbooks.common.rich_utils import console
|
80
80
|
from Inventory_Modules import display_results, get_all_credentials
|
81
81
|
|
82
|
-
init()
|
83
82
|
__version__ = "2024.10.24"
|
84
83
|
|
85
84
|
# TODO: Add Elastic IPs to this script as well.
|
@@ -384,7 +383,6 @@ def analyze_results(fSubnetsFound: list):
|
|
384
383
|
##################
|
385
384
|
|
386
385
|
|
387
|
-
ERASE_LINE = "\x1b[2K"
|
388
386
|
begin_time = time()
|
389
387
|
|
390
388
|
if __name__ == "__main__":
|
@@ -426,7 +424,7 @@ if __name__ == "__main__":
|
|
426
424
|
analyze_results(SubnetsFound)
|
427
425
|
if pTiming:
|
428
426
|
print(ERASE_LINE)
|
429
|
-
print(f"
|
427
|
+
print(f"[green]This script completed in {time() - begin_time:.2f} seconds")
|
430
428
|
|
431
429
|
print()
|
432
430
|
print("Thank you for using this script")
|
runbooks/inventory/list_vpcs.py
CHANGED
@@ -62,10 +62,9 @@ from time import time
|
|
62
62
|
import Inventory_Modules
|
63
63
|
from ArgumentsClass import CommonArguments
|
64
64
|
from botocore.exceptions import ClientError
|
65
|
-
from colorama import Fore, init
|
66
65
|
from Inventory_Modules import display_results, get_all_credentials
|
66
|
+
from runbooks.common.rich_utils import console
|
67
67
|
|
68
|
-
init()
|
69
68
|
__version__ = "2024.01.26"
|
70
69
|
|
71
70
|
|
@@ -324,9 +323,9 @@ def find_all_vpcs(fAllCredentials, fDefaultOnly=False):
|
|
324
323
|
|
325
324
|
for credential in fAllCredentials:
|
326
325
|
logging.info(f"Beginning to queue data - starting with {credential['AccountId']}")
|
327
|
-
print(
|
328
|
-
f"
|
329
|
-
end="
|
326
|
+
console.print(
|
327
|
+
f"Checking {credential['AccountId']} in region {credential['Region']} - {PlaceCount + 1} / {len(fAllCredentials)}",
|
328
|
+
end="",
|
330
329
|
)
|
331
330
|
# for region in fRegionList:
|
332
331
|
try:
|
@@ -361,8 +360,6 @@ if __name__ == "__main__":
|
|
361
360
|
verbose = args.loglevel
|
362
361
|
logging.basicConfig(level=verbose, format="[%(filename)s:%(lineno)s - %(funcName)30s() ] %(message)s")
|
363
362
|
|
364
|
-
ERASE_LINE = "\x1b[2K"
|
365
|
-
|
366
363
|
begin_time = time()
|
367
364
|
|
368
365
|
NumVpcsFound = 0
|
@@ -409,9 +406,9 @@ if __name__ == "__main__":
|
|
409
406
|
logging.info(f"Threads all done - took {time() - begin_time:.2f} seconds")
|
410
407
|
|
411
408
|
if pTiming:
|
412
|
-
print(
|
413
|
-
print(f"
|
414
|
-
print(
|
409
|
+
console.print()
|
410
|
+
console.print(f"[green]This script took {time() - begin_time:.2f} seconds[/green]")
|
411
|
+
console.print()
|
415
412
|
# Had to do this, because some of the VPCs that show up in the "sorted_AllVPCs" list are actually the same VPC, with a different CIDR range.
|
416
413
|
Num_of_unique_VPCs = len(set([x["VpcId"] for x in sorted_AllVPCs]))
|
417
414
|
print(
|
@@ -35,12 +35,13 @@ from pathlib import Path
|
|
35
35
|
from typing import Any, Dict, List, Optional, Tuple, Union
|
36
36
|
|
37
37
|
import boto3
|
38
|
-
from rich.
|
39
|
-
from
|
38
|
+
from rich.progress import BarColumn, SpinnerColumn, TaskProgressColumn, TextColumn, TimeElapsedColumn
|
39
|
+
from runbooks.common.rich_utils import Progress
|
40
40
|
from rich.table import Table
|
41
41
|
|
42
42
|
from ..common.profile_utils import get_profile_for_operation, resolve_profile_for_operation_silent
|
43
43
|
from ..common.rich_utils import (
|
44
|
+
Console,
|
44
45
|
console as rich_console,
|
45
46
|
create_table,
|
46
47
|
format_cost,
|
@@ -1003,7 +1004,8 @@ class EnhancedMCPValidator:
|
|
1003
1004
|
with ThreadPoolExecutor(max_workers=min(5, len(self.aws_sessions))) as executor:
|
1004
1005
|
# Submit all validation tasks
|
1005
1006
|
future_to_profile = {}
|
1006
|
-
for profile,
|
1007
|
+
for profile, session_info in self.aws_sessions.items():
|
1008
|
+
session = session_info["session"] # Extract boto3.Session from dict
|
1007
1009
|
future = executor.submit(
|
1008
1010
|
self._validate_profile_with_drift_detection, profile, session, runbooks_inventory
|
1009
1011
|
)
|
@@ -24,17 +24,21 @@ from typing import Dict, List, Optional, Set, Tuple
|
|
24
24
|
|
25
25
|
import boto3
|
26
26
|
from botocore.exceptions import ClientError, NoCredentialsError
|
27
|
-
from rich.console import Console
|
28
27
|
from rich.panel import Panel
|
29
|
-
from rich.progress import BarColumn,
|
28
|
+
from rich.progress import BarColumn, SpinnerColumn, TextColumn, TimeElapsedColumn
|
30
29
|
from rich.status import Status
|
31
30
|
from rich.table import Table
|
32
31
|
|
33
|
-
#
|
34
|
-
|
32
|
+
# Test Mode Support: Use centralized rich_utils for console and Progress (test-aware)
|
33
|
+
# Issue: Rich Console/Progress write to StringIO buffer that Click CliRunner closes, causing ValueError
|
34
|
+
# Solution: rich_utils provides MockConsole/MockProgress in test mode, Rich components in production
|
35
|
+
import os
|
36
|
+
import sys
|
37
|
+
import re
|
35
38
|
|
36
39
|
from ..utils.logger import configure_logger
|
37
40
|
from ..common.performance_optimization_engine import get_optimization_engine
|
41
|
+
from ..common.rich_utils import console, Progress
|
38
42
|
|
39
43
|
logger = configure_logger(__name__)
|
40
44
|
|
@@ -10,7 +10,7 @@ import Inventory_Modules
|
|
10
10
|
import simplejson as json
|
11
11
|
from account_class import aws_acct_access
|
12
12
|
from ArgumentsClass import CommonArguments
|
13
|
-
from
|
13
|
+
from runbooks.common.rich_utils import console
|
14
14
|
from Inventory_Modules import get_credentials_for_accounts_in_org
|
15
15
|
|
16
16
|
"""
|
@@ -18,7 +18,6 @@ This script was created to help solve a testing problem for the "move_stack_inst
|
|
18
18
|
Originally, that script didn't have built-in recovery, so we needed this script to "recover" those stack-instance ids that might have been lost during the move_stack_instances.py run. However, that script now has built-in recovery, so this script isn't really needed. However, it can still be used to find any stack-instances that have been orphaned from their original stack-set, if that happens.
|
19
19
|
"""
|
20
20
|
|
21
|
-
init()
|
22
21
|
__version__ = "2024.05.18"
|
23
22
|
|
24
23
|
|
@@ -70,9 +69,9 @@ def setup_auth_and_regions(fProfile: str = None) -> (object, list, list):
|
|
70
69
|
ChildAccounts = Inventory_Modules.RemoveCoreAccounts(ChildAccounts, pAccountsToSkip)
|
71
70
|
AccountList = [account["AccountId"] for account in ChildAccounts]
|
72
71
|
|
73
|
-
print(f"You asked to find stacks with this fragment list: {
|
74
|
-
print(f"\t\tin these accounts: {
|
75
|
-
print(f"\t\tin these regions: {
|
72
|
+
print(f"You asked to find stacks with this fragment list: [red]{pFragments}")
|
73
|
+
print(f"\t\tin these accounts: [red]{AccountList}")
|
74
|
+
print(f"\t\tin these regions: [red]{RegionList}")
|
76
75
|
return aws_acct, AccountList, RegionList
|
77
76
|
|
78
77
|
|
@@ -129,7 +128,7 @@ if __name__ == "__main__":
|
|
129
128
|
Stacks = Inventory_Modules.find_stacks2(cred, region, pFragments)
|
130
129
|
logging.warning(f"Account: {cred['AccountId']} | Region: {region} | Found {len(Stacks)} Stacks")
|
131
130
|
print(
|
132
|
-
f"{ERASE_LINE}
|
131
|
+
f"{ERASE_LINE}[red]Account: {cred['AccountId']} Region: {region} Found {len(Stacks)} Stacks ({item_counter} of {len(AccountList) * len(RegionList)})",
|
133
132
|
end="\r",
|
134
133
|
)
|
135
134
|
except Exception as my_Error:
|
@@ -162,7 +161,7 @@ if __name__ == "__main__":
|
|
162
161
|
lAccountsAndRegions.append((StacksFound[i]["Account"], StacksFound[i]["Region"]))
|
163
162
|
print(ERASE_LINE)
|
164
163
|
print(
|
165
|
-
f"
|
164
|
+
f"[red]Looked through {len(StacksFound)} Stacks across {len(AccountList)} accounts across {len(RegionList)} regions"
|
166
165
|
)
|
167
166
|
print()
|
168
167
|
if args.loglevel < 21: # INFO level
|
@@ -198,7 +197,7 @@ if __name__ == "__main__":
|
|
198
197
|
|
199
198
|
if pTiming:
|
200
199
|
print(ERASE_LINE)
|
201
|
-
print(f"
|
200
|
+
print(f"[green]This script took {time() - begin_time:.2f} seconds")
|
202
201
|
|
203
202
|
print()
|
204
203
|
print("Thanks for using this script...")
|
@@ -21,9 +21,9 @@ from datetime import datetime
|
|
21
21
|
from typing import Any, Dict, List, Optional
|
22
22
|
|
23
23
|
from rich import box
|
24
|
-
from rich.console import Console
|
25
24
|
from rich.panel import Panel
|
26
|
-
from rich.progress import BarColumn,
|
25
|
+
from rich.progress import BarColumn, SpinnerColumn, TaskProgressColumn, TextColumn
|
26
|
+
from runbooks.common.rich_utils import Progress
|
27
27
|
from rich.table import Table
|
28
28
|
from rich.text import Text
|
29
29
|
from rich.tree import Tree
|
@@ -7,14 +7,12 @@ import Inventory_Modules
|
|
7
7
|
from account_class import aws_acct_access
|
8
8
|
from ArgumentsClass import CommonArguments
|
9
9
|
from botocore.exceptions import ClientError
|
10
|
-
from
|
11
|
-
from rich.console import Console
|
10
|
+
from runbooks.common.rich_utils import console
|
12
11
|
from rich.panel import Panel
|
13
12
|
|
14
|
-
# Initialize Rich console
|
15
|
-
|
13
|
+
# Initialize Rich console with test mode support
|
14
|
+
from runbooks.common.rich_utils import console
|
16
15
|
|
17
|
-
init()
|
18
16
|
__version__ = "2023.05.04"
|
19
17
|
|
20
18
|
parser = CommonArguments()
|
@@ -38,12 +38,13 @@ from pathlib import Path
|
|
38
38
|
from typing import Any, Dict, List, Optional, Tuple, Union
|
39
39
|
|
40
40
|
import boto3
|
41
|
-
from rich.
|
42
|
-
from
|
41
|
+
from rich.progress import BarColumn, SpinnerColumn, TaskProgressColumn, TextColumn, TimeElapsedColumn
|
42
|
+
from runbooks.common.rich_utils import Progress
|
43
43
|
from rich.table import Table
|
44
44
|
|
45
45
|
from ..common.profile_utils import get_profile_for_operation, resolve_profile_for_operation_silent
|
46
46
|
from ..common.rich_utils import (
|
47
|
+
Console,
|
47
48
|
console as rich_console,
|
48
49
|
create_table,
|
49
50
|
format_cost,
|
@@ -34,9 +34,9 @@ import asyncio
|
|
34
34
|
|
35
35
|
import boto3
|
36
36
|
from botocore.exceptions import ClientError
|
37
|
-
from rich.console import Console
|
38
37
|
from rich.panel import Panel
|
39
|
-
from rich.progress import
|
38
|
+
from rich.progress import SpinnerColumn, TextColumn
|
39
|
+
from runbooks.common.rich_utils import Progress
|
40
40
|
from rich.table import Table
|
41
41
|
from rich.tree import Tree
|
42
42
|
|
@@ -44,6 +44,7 @@ from runbooks.common.profile_utils import create_operational_session
|
|
44
44
|
from runbooks.common.cross_account_manager import EnhancedCrossAccountManager, CrossAccountSession
|
45
45
|
from runbooks.common.organizations_client import OrganizationAccount, get_unified_organizations_client
|
46
46
|
from runbooks.common.rich_utils import (
|
47
|
+
Console,
|
47
48
|
console,
|
48
49
|
print_header,
|
49
50
|
print_success,
|
@@ -43,10 +43,10 @@ from typing import Any, Dict, List, Optional, Set, Tuple
|
|
43
43
|
import hashlib
|
44
44
|
import boto3
|
45
45
|
from botocore.exceptions import ClientError
|
46
|
-
from rich.console import Console
|
47
46
|
from rich.table import Table
|
48
47
|
from rich.panel import Panel
|
49
|
-
from rich.progress import
|
48
|
+
from rich.progress import SpinnerColumn, TextColumn
|
49
|
+
from runbooks.common.rich_utils import Progress
|
50
50
|
|
51
51
|
from runbooks.common.rich_utils import (
|
52
52
|
console,
|
@@ -242,6 +242,15 @@ class TerraformDriftDetector:
|
|
242
242
|
drift_analysis, runbooks_resources, terraform_resources
|
243
243
|
)
|
244
244
|
|
245
|
+
# CRITICAL: Enforce quality gate - block on <99.5% accuracy
|
246
|
+
accuracy_score = mcp_validation_result.get("accuracy_score", 0.0)
|
247
|
+
if accuracy_score < 99.5:
|
248
|
+
error_msg = f"MCP validation accuracy {accuracy_score:.1f}% below required threshold 99.5%"
|
249
|
+
console.print(f"[red]❌ MCP Validation FAILED: {accuracy_score:.1f}% < 99.5% required[/red]")
|
250
|
+
raise ValueError(error_msg)
|
251
|
+
|
252
|
+
console.print(f"[green]✅ MCP Validation PASSED: {accuracy_score:.1f}% ≥ 99.5% required[/green]")
|
253
|
+
|
245
254
|
# Calculate metrics
|
246
255
|
total_tf = len(terraform_resources)
|
247
256
|
total_rb = len(runbooks_resources)
|
@@ -272,7 +281,7 @@ class TerraformDriftDetector:
|
|
272
281
|
total_monthly_cost_impact=cost_metrics.get("total_monthly_cost", 0.0),
|
273
282
|
high_cost_drifts=cost_metrics.get("high_cost_drifts", 0),
|
274
283
|
cost_correlation_coverage=cost_metrics.get("correlation_coverage", 0.0),
|
275
|
-
mcp_validation_accuracy=mcp_validation_result.get("accuracy_score",
|
284
|
+
mcp_validation_accuracy=mcp_validation_result.get("accuracy_score", 0.0), # Pessimistic default
|
276
285
|
# Analysis details
|
277
286
|
drift_analysis=drift_analysis,
|
278
287
|
missing_from_terraform=[
|
@@ -808,24 +817,26 @@ class TerraformDriftDetector:
|
|
808
817
|
"validation_timestamp": datetime.now().isoformat(),
|
809
818
|
}
|
810
819
|
|
811
|
-
# Run MCP validation
|
820
|
+
# Run MCP validation with real accuracy calculation
|
812
821
|
validation_result = await self.mcp_integrator.validate_vpc_operations(validation_data)
|
813
822
|
|
814
|
-
#
|
815
|
-
accuracy_score =
|
823
|
+
# Use real accuracy from MCP validation (no hardcoded values)
|
824
|
+
accuracy_score = validation_result.accuracy_score # Real calculated accuracy
|
816
825
|
|
817
826
|
return {
|
818
827
|
"success": validation_result.success,
|
819
828
|
"accuracy_score": accuracy_score,
|
820
829
|
"validation_timestamp": datetime.now().isoformat(),
|
821
830
|
"resources_validated": len(drift_analysis),
|
831
|
+
"total_validations": validation_result.performance_metrics.get("total_validations", 0),
|
832
|
+
"successful_validations": validation_result.performance_metrics.get("successful_validations", 0),
|
822
833
|
}
|
823
834
|
|
824
835
|
except Exception as e:
|
825
836
|
print_warning(f"MCP validation error: {str(e)[:50]}...")
|
826
837
|
return {
|
827
838
|
"success": False,
|
828
|
-
"accuracy_score":
|
839
|
+
"accuracy_score": 0.0, # Honest failure - no optimistic defaults
|
829
840
|
"validation_timestamp": datetime.now().isoformat(),
|
830
841
|
"error": str(e),
|
831
842
|
}
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: runbooks
|
3
|
-
Version: 1.1.
|
3
|
+
Version: 1.1.7
|
4
4
|
Summary: CloudOps Automation Toolkit with Enhanced Cloud Foundations Assessment for DevOps and SRE teams.
|
5
5
|
Author-email: Maintainers <nnthanh101@gmail.com>
|
6
6
|
License-Expression: Apache-2.0
|
@@ -31,19 +31,18 @@ Requires-Dist: pydantic>=2.10.0
|
|
31
31
|
Requires-Dist: jinja2>=3.1.4
|
32
32
|
Requires-Dist: werkzeug>=3.1.0
|
33
33
|
Requires-Dist: markdown>=3.7.0
|
34
|
-
Requires-Dist: prettytable>=3.16.0
|
35
34
|
Requires-Dist: simplejson>=3.20.1
|
36
35
|
Requires-Dist: python-dateutil>=2.9.0
|
37
36
|
Requires-Dist: loguru>=0.7.3
|
38
|
-
Requires-Dist: tqdm>=4.67.1
|
39
37
|
Requires-Dist: graphviz>=0.20.1
|
40
|
-
Requires-Dist: rich>=14.
|
38
|
+
Requires-Dist: rich>=14.1.0
|
41
39
|
Requires-Dist: reportlab>=3.6.1
|
42
40
|
Requires-Dist: requests>=2.32.0
|
43
41
|
Requires-Dist: packaging>=21.0
|
44
42
|
Requires-Dist: pyyaml>=6.0.2
|
45
43
|
Requires-Dist: jmespath>=1.0.1
|
46
44
|
Requires-Dist: urllib3<1.27,>=1.26.18
|
45
|
+
Requires-Dist: openapi-spec-validator>=0.7.2
|
47
46
|
Requires-Dist: mcp>=1.12.3
|
48
47
|
Requires-Dist: pandas>=2.3.1
|
49
48
|
Requires-Dist: ipython>=9.4.0
|