runbooks 0.2.5__py3-none-any.whl → 0.6.1__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.
- conftest.py +26 -0
- jupyter-agent/.env.template +2 -0
- jupyter-agent/.gitattributes +35 -0
- jupyter-agent/README.md +16 -0
- jupyter-agent/app.py +256 -0
- jupyter-agent/cloudops-agent.png +0 -0
- jupyter-agent/ds-system-prompt.txt +154 -0
- jupyter-agent/jupyter-agent.png +0 -0
- jupyter-agent/llama3_template.jinja +123 -0
- jupyter-agent/requirements.txt +9 -0
- jupyter-agent/utils.py +409 -0
- runbooks/__init__.py +71 -3
- runbooks/__main__.py +13 -0
- runbooks/aws/ec2_describe_instances.py +1 -1
- runbooks/aws/ec2_run_instances.py +8 -2
- runbooks/aws/ec2_start_stop_instances.py +17 -4
- runbooks/aws/ec2_unused_volumes.py +5 -1
- runbooks/aws/s3_create_bucket.py +4 -2
- runbooks/aws/s3_list_objects.py +6 -1
- runbooks/aws/tagging_lambda_handler.py +13 -2
- runbooks/aws/tags.json +12 -0
- runbooks/base.py +353 -0
- runbooks/cfat/README.md +49 -0
- runbooks/cfat/__init__.py +74 -0
- runbooks/cfat/app.ts +644 -0
- runbooks/cfat/assessment/__init__.py +40 -0
- runbooks/cfat/assessment/asana-import.csv +39 -0
- runbooks/cfat/assessment/cfat-checks.csv +31 -0
- runbooks/cfat/assessment/cfat.txt +520 -0
- runbooks/cfat/assessment/collectors.py +200 -0
- runbooks/cfat/assessment/jira-import.csv +39 -0
- runbooks/cfat/assessment/runner.py +387 -0
- runbooks/cfat/assessment/validators.py +290 -0
- runbooks/cfat/cli.py +103 -0
- runbooks/cfat/docs/asana-import.csv +24 -0
- runbooks/cfat/docs/cfat-checks.csv +31 -0
- runbooks/cfat/docs/cfat.txt +335 -0
- runbooks/cfat/docs/checks-output.png +0 -0
- runbooks/cfat/docs/cloudshell-console-run.png +0 -0
- runbooks/cfat/docs/cloudshell-download.png +0 -0
- runbooks/cfat/docs/cloudshell-output.png +0 -0
- runbooks/cfat/docs/downloadfile.png +0 -0
- runbooks/cfat/docs/jira-import.csv +24 -0
- runbooks/cfat/docs/open-cloudshell.png +0 -0
- runbooks/cfat/docs/report-header.png +0 -0
- runbooks/cfat/models.py +1026 -0
- runbooks/cfat/package-lock.json +5116 -0
- runbooks/cfat/package.json +38 -0
- runbooks/cfat/report.py +496 -0
- runbooks/cfat/reporting/__init__.py +46 -0
- runbooks/cfat/reporting/exporters.py +337 -0
- runbooks/cfat/reporting/formatters.py +496 -0
- runbooks/cfat/reporting/templates.py +135 -0
- runbooks/cfat/run-assessment.sh +23 -0
- runbooks/cfat/runner.py +69 -0
- runbooks/cfat/src/actions/check-cloudtrail-existence.ts +43 -0
- runbooks/cfat/src/actions/check-config-existence.ts +37 -0
- runbooks/cfat/src/actions/check-control-tower.ts +37 -0
- runbooks/cfat/src/actions/check-ec2-existence.ts +46 -0
- runbooks/cfat/src/actions/check-iam-users.ts +50 -0
- runbooks/cfat/src/actions/check-legacy-cur.ts +30 -0
- runbooks/cfat/src/actions/check-org-cloudformation.ts +30 -0
- runbooks/cfat/src/actions/check-vpc-existence.ts +43 -0
- runbooks/cfat/src/actions/create-asanaimport.ts +14 -0
- runbooks/cfat/src/actions/create-backlog.ts +372 -0
- runbooks/cfat/src/actions/create-jiraimport.ts +15 -0
- runbooks/cfat/src/actions/create-report.ts +616 -0
- runbooks/cfat/src/actions/define-account-type.ts +51 -0
- runbooks/cfat/src/actions/get-enabled-org-policy-types.ts +40 -0
- runbooks/cfat/src/actions/get-enabled-org-services.ts +26 -0
- runbooks/cfat/src/actions/get-idc-info.ts +34 -0
- runbooks/cfat/src/actions/get-org-da-accounts.ts +34 -0
- runbooks/cfat/src/actions/get-org-details.ts +35 -0
- runbooks/cfat/src/actions/get-org-member-accounts.ts +44 -0
- runbooks/cfat/src/actions/get-org-ous.ts +35 -0
- runbooks/cfat/src/actions/get-regions.ts +22 -0
- runbooks/cfat/src/actions/zip-assessment.ts +27 -0
- runbooks/cfat/src/types/index.d.ts +147 -0
- runbooks/cfat/tests/__init__.py +141 -0
- runbooks/cfat/tests/test_cli.py +340 -0
- runbooks/cfat/tests/test_integration.py +290 -0
- runbooks/cfat/tests/test_models.py +505 -0
- runbooks/cfat/tests/test_reporting.py +354 -0
- runbooks/cfat/tsconfig.json +16 -0
- runbooks/cfat/webpack.config.cjs +27 -0
- runbooks/config.py +260 -0
- runbooks/finops/__init__.py +88 -0
- runbooks/finops/aws_client.py +245 -0
- runbooks/finops/cli.py +151 -0
- runbooks/finops/cost_processor.py +410 -0
- runbooks/finops/dashboard_runner.py +448 -0
- runbooks/finops/helpers.py +355 -0
- runbooks/finops/main.py +14 -0
- runbooks/finops/profile_processor.py +174 -0
- runbooks/finops/types.py +66 -0
- runbooks/finops/visualisations.py +80 -0
- runbooks/inventory/.gitignore +354 -0
- runbooks/inventory/ArgumentsClass.py +261 -0
- runbooks/inventory/Inventory_Modules.py +6130 -0
- runbooks/inventory/LandingZone/delete_lz.py +1075 -0
- runbooks/inventory/README.md +1320 -0
- runbooks/inventory/__init__.py +62 -0
- runbooks/inventory/account_class.py +532 -0
- runbooks/inventory/all_my_instances_wrapper.py +123 -0
- runbooks/inventory/aws_decorators.py +201 -0
- runbooks/inventory/cfn_move_stack_instances.py +1526 -0
- runbooks/inventory/check_cloudtrail_compliance.py +614 -0
- runbooks/inventory/check_controltower_readiness.py +1107 -0
- runbooks/inventory/check_landingzone_readiness.py +711 -0
- runbooks/inventory/cloudtrail.md +727 -0
- runbooks/inventory/collectors/__init__.py +20 -0
- runbooks/inventory/collectors/aws_compute.py +518 -0
- runbooks/inventory/collectors/aws_networking.py +275 -0
- runbooks/inventory/collectors/base.py +222 -0
- runbooks/inventory/core/__init__.py +19 -0
- runbooks/inventory/core/collector.py +303 -0
- runbooks/inventory/core/formatter.py +296 -0
- runbooks/inventory/delete_s3_buckets_objects.py +169 -0
- runbooks/inventory/discovery.md +81 -0
- runbooks/inventory/draw_org_structure.py +748 -0
- runbooks/inventory/ec2_vpc_utils.py +341 -0
- runbooks/inventory/find_cfn_drift_detection.py +272 -0
- runbooks/inventory/find_cfn_orphaned_stacks.py +719 -0
- runbooks/inventory/find_cfn_stackset_drift.py +733 -0
- runbooks/inventory/find_ec2_security_groups.py +669 -0
- runbooks/inventory/find_landingzone_versions.py +201 -0
- runbooks/inventory/find_vpc_flow_logs.py +1221 -0
- runbooks/inventory/inventory.sh +659 -0
- runbooks/inventory/list_cfn_stacks.py +558 -0
- runbooks/inventory/list_cfn_stackset_operation_results.py +252 -0
- runbooks/inventory/list_cfn_stackset_operations.py +734 -0
- runbooks/inventory/list_cfn_stacksets.py +453 -0
- runbooks/inventory/list_config_recorders_delivery_channels.py +681 -0
- runbooks/inventory/list_ds_directories.py +354 -0
- runbooks/inventory/list_ec2_availability_zones.py +286 -0
- runbooks/inventory/list_ec2_ebs_volumes.py +244 -0
- runbooks/inventory/list_ec2_instances.py +425 -0
- runbooks/inventory/list_ecs_clusters_and_tasks.py +562 -0
- runbooks/inventory/list_elbs_load_balancers.py +411 -0
- runbooks/inventory/list_enis_network_interfaces.py +526 -0
- runbooks/inventory/list_guardduty_detectors.py +568 -0
- runbooks/inventory/list_iam_policies.py +404 -0
- runbooks/inventory/list_iam_roles.py +518 -0
- runbooks/inventory/list_iam_saml_providers.py +359 -0
- runbooks/inventory/list_lambda_functions.py +882 -0
- runbooks/inventory/list_org_accounts.py +446 -0
- runbooks/inventory/list_org_accounts_users.py +354 -0
- runbooks/inventory/list_rds_db_instances.py +406 -0
- runbooks/inventory/list_route53_hosted_zones.py +318 -0
- runbooks/inventory/list_servicecatalog_provisioned_products.py +575 -0
- runbooks/inventory/list_sns_topics.py +360 -0
- runbooks/inventory/list_ssm_parameters.py +402 -0
- runbooks/inventory/list_vpc_subnets.py +433 -0
- runbooks/inventory/list_vpcs.py +422 -0
- runbooks/inventory/lockdown_cfn_stackset_role.py +224 -0
- runbooks/inventory/models/__init__.py +24 -0
- runbooks/inventory/models/account.py +192 -0
- runbooks/inventory/models/inventory.py +309 -0
- runbooks/inventory/models/resource.py +247 -0
- runbooks/inventory/recover_cfn_stack_ids.py +205 -0
- runbooks/inventory/requirements.txt +12 -0
- runbooks/inventory/run_on_multi_accounts.py +211 -0
- runbooks/inventory/tests/common_test_data.py +3661 -0
- runbooks/inventory/tests/common_test_functions.py +204 -0
- runbooks/inventory/tests/script_test_data.py +0 -0
- runbooks/inventory/tests/setup.py +24 -0
- runbooks/inventory/tests/src.py +18 -0
- runbooks/inventory/tests/test_cfn_describe_stacks.py +208 -0
- runbooks/inventory/tests/test_ec2_describe_instances.py +162 -0
- runbooks/inventory/tests/test_inventory_modules.py +55 -0
- runbooks/inventory/tests/test_lambda_list_functions.py +86 -0
- runbooks/inventory/tests/test_moto_integration_example.py +273 -0
- runbooks/inventory/tests/test_org_list_accounts.py +49 -0
- runbooks/inventory/update_aws_actions.py +173 -0
- runbooks/inventory/update_cfn_stacksets.py +1215 -0
- runbooks/inventory/update_cloudwatch_logs_retention_policy.py +294 -0
- runbooks/inventory/update_iam_roles_cross_accounts.py +478 -0
- runbooks/inventory/update_s3_public_access_block.py +539 -0
- runbooks/inventory/utils/__init__.py +23 -0
- runbooks/inventory/utils/aws_helpers.py +510 -0
- runbooks/inventory/utils/threading_utils.py +493 -0
- runbooks/inventory/utils/validation.py +682 -0
- runbooks/inventory/verify_ec2_security_groups.py +1430 -0
- runbooks/main.py +785 -0
- runbooks/organizations/__init__.py +12 -0
- runbooks/organizations/manager.py +374 -0
- runbooks/security_baseline/README.md +324 -0
- runbooks/security_baseline/checklist/alternate_contacts.py +8 -1
- runbooks/security_baseline/checklist/bucket_public_access.py +4 -1
- runbooks/security_baseline/checklist/cloudwatch_alarm_configuration.py +9 -2
- runbooks/security_baseline/checklist/guardduty_enabled.py +9 -2
- runbooks/security_baseline/checklist/multi_region_instance_usage.py +5 -1
- runbooks/security_baseline/checklist/root_access_key.py +6 -1
- runbooks/security_baseline/config-origin.json +1 -1
- runbooks/security_baseline/config.json +1 -1
- runbooks/security_baseline/permission.json +1 -1
- runbooks/security_baseline/report_generator.py +10 -2
- runbooks/security_baseline/report_template_en.html +7 -7
- runbooks/security_baseline/report_template_jp.html +7 -7
- runbooks/security_baseline/report_template_kr.html +12 -12
- runbooks/security_baseline/report_template_vn.html +7 -7
- runbooks/security_baseline/requirements.txt +7 -0
- runbooks/security_baseline/run_script.py +8 -2
- runbooks/security_baseline/security_baseline_tester.py +10 -2
- runbooks/security_baseline/utils/common.py +5 -1
- runbooks/utils/__init__.py +204 -0
- runbooks-0.6.1.dist-info/METADATA +373 -0
- runbooks-0.6.1.dist-info/RECORD +237 -0
- {runbooks-0.2.5.dist-info → runbooks-0.6.1.dist-info}/WHEEL +1 -1
- runbooks-0.6.1.dist-info/entry_points.txt +7 -0
- runbooks-0.6.1.dist-info/licenses/LICENSE +201 -0
- runbooks-0.6.1.dist-info/top_level.txt +3 -0
- runbooks/python101/calculator.py +0 -34
- runbooks/python101/config.py +0 -1
- runbooks/python101/exceptions.py +0 -16
- runbooks/python101/file_manager.py +0 -218
- runbooks/python101/toolkit.py +0 -153
- runbooks-0.2.5.dist-info/METADATA +0 -439
- runbooks-0.2.5.dist-info/RECORD +0 -61
- runbooks-0.2.5.dist-info/entry_points.txt +0 -3
- runbooks-0.2.5.dist-info/top_level.txt +0 -1
jupyter-agent/utils.py
ADDED
@@ -0,0 +1,409 @@
|
|
1
|
+
"""
|
2
|
+
utils.py
|
3
|
+
|
4
|
+
This module provides helper functions to:
|
5
|
+
- Generate and update Jupyter Notebook structures.
|
6
|
+
- Execute code in a sandbox environment.
|
7
|
+
- Parse and convert execution results to notebook cell outputs.
|
8
|
+
- Export notebooks to HTML.
|
9
|
+
"""
|
10
|
+
|
11
|
+
import json
|
12
|
+
from pathlib import Path
|
13
|
+
from typing import Any, Dict, List, Tuple
|
14
|
+
|
15
|
+
import nbformat
|
16
|
+
from e2b_code_interpreter import Sandbox
|
17
|
+
from huggingface_hub import InferenceClient
|
18
|
+
from nbconvert import HTMLExporter
|
19
|
+
from nbformat.v4 import new_code_cell, new_markdown_cell, new_notebook
|
20
|
+
from traitlets.config import Config
|
21
|
+
from transformers import AutoTokenizer
|
22
|
+
|
23
|
+
## --- Global Configuration and Template Loading ---
|
24
|
+
|
25
|
+
## Create a configuration for nbconvert and set up the HTML exporter
|
26
|
+
config = Config()
|
27
|
+
html_exporter = HTMLExporter(config=config, template_name="classic")
|
28
|
+
|
29
|
+
## Load the Jinja template for the LLaMA model
|
30
|
+
TEMPLATE_PATH = Path("llama3_template.jinja")
|
31
|
+
try:
|
32
|
+
with TEMPLATE_PATH.open("r", encoding="utf-8") as f:
|
33
|
+
llama_template = f.read()
|
34
|
+
except FileNotFoundError:
|
35
|
+
raise FileNotFoundError(f"Template file {TEMPLATE_PATH} not found.")
|
36
|
+
|
37
|
+
MAX_TURNS = 4
|
38
|
+
|
39
|
+
## --- Code Execution Functions ---
|
40
|
+
|
41
|
+
|
42
|
+
def parse_exec_result_nb(execution: Any) -> List[Dict[str, Any]]:
|
43
|
+
"""
|
44
|
+
Convert an E2B execution object to a list of Jupyter notebook cell outputs format.
|
45
|
+
|
46
|
+
:param execution: Execution object from the sandbox.
|
47
|
+
:return: List of output dictionaries.
|
48
|
+
"""
|
49
|
+
outputs: List[Dict[str, Any]] = []
|
50
|
+
|
51
|
+
if execution.logs.stdout:
|
52
|
+
outputs.append(
|
53
|
+
{
|
54
|
+
"output_type": "stream",
|
55
|
+
"name": "stdout",
|
56
|
+
"text": "".join(execution.logs.stdout),
|
57
|
+
}
|
58
|
+
)
|
59
|
+
|
60
|
+
if execution.logs.stderr:
|
61
|
+
outputs.append(
|
62
|
+
{
|
63
|
+
"output_type": "stream",
|
64
|
+
"name": "stderr",
|
65
|
+
"text": "".join(execution.logs.stderr),
|
66
|
+
}
|
67
|
+
)
|
68
|
+
|
69
|
+
if execution.error:
|
70
|
+
outputs.append(
|
71
|
+
{
|
72
|
+
"output_type": "error",
|
73
|
+
"ename": execution.error.name,
|
74
|
+
"evalue": execution.error.value,
|
75
|
+
"traceback": [line for line in execution.error.traceback.split("\n")],
|
76
|
+
}
|
77
|
+
)
|
78
|
+
|
79
|
+
for result in execution.results:
|
80
|
+
output = {
|
81
|
+
"output_type": (
|
82
|
+
"execute_result" if result.is_main_result else "display_data"
|
83
|
+
),
|
84
|
+
"metadata": {},
|
85
|
+
"data": {},
|
86
|
+
}
|
87
|
+
|
88
|
+
if result.text:
|
89
|
+
output["data"]["text/plain"] = [result.text] # Array for text/plain
|
90
|
+
if result.html:
|
91
|
+
output["data"]["text/html"] = result.html
|
92
|
+
if result.png:
|
93
|
+
output["data"]["image/png"] = result.png
|
94
|
+
if result.svg:
|
95
|
+
output["data"]["image/svg+xml"] = result.svg
|
96
|
+
if result.jpeg:
|
97
|
+
output["data"]["image/jpeg"] = result.jpeg
|
98
|
+
if result.pdf:
|
99
|
+
output["data"]["application/pdf"] = result.pdf
|
100
|
+
if result.latex:
|
101
|
+
output["data"]["text/latex"] = result.latex
|
102
|
+
if result.json:
|
103
|
+
output["data"]["application/json"] = result.json
|
104
|
+
if result.javascript:
|
105
|
+
output["data"]["application/javascript"] = result.javascript
|
106
|
+
|
107
|
+
if result.is_main_result and execution.execution_count is not None:
|
108
|
+
output["execution_count"] = execution.execution_count
|
109
|
+
|
110
|
+
if output["data"]:
|
111
|
+
outputs.append(output)
|
112
|
+
|
113
|
+
return outputs
|
114
|
+
|
115
|
+
|
116
|
+
## HTML and CSS templates for notebook cells
|
117
|
+
system_template = """\
|
118
|
+
<details>
|
119
|
+
<summary style="display: flex; align-items: center;">
|
120
|
+
<div class="alert alert-block alert-info" style="margin: 0; width: 100%;">
|
121
|
+
<b>System: <span class="arrow">▶</span></b>
|
122
|
+
</div>
|
123
|
+
</summary>
|
124
|
+
<div class="alert alert-block alert-info">
|
125
|
+
{}
|
126
|
+
</div>
|
127
|
+
</details>
|
128
|
+
|
129
|
+
<style>
|
130
|
+
details > summary .arrow {{
|
131
|
+
display: inline-block;
|
132
|
+
transition: transform 0.2s;
|
133
|
+
}}
|
134
|
+
details[open] > summary .arrow {{
|
135
|
+
transform: rotate(90deg);
|
136
|
+
}}
|
137
|
+
</style>
|
138
|
+
"""
|
139
|
+
|
140
|
+
user_template = """<div class="alert alert-block alert-success">
|
141
|
+
<b>User:</b> {}
|
142
|
+
</div>
|
143
|
+
"""
|
144
|
+
|
145
|
+
header_message = """<p align="center">
|
146
|
+
<img src="cloudops-agent.png" alt="Jupyter Agent" />
|
147
|
+
</p>
|
148
|
+
|
149
|
+
|
150
|
+
<p style="text-align:center;">Let a LLM agent write and execute code inside a notebook!</p>"""
|
151
|
+
|
152
|
+
bad_html_bad = """input[type="file"] {
|
153
|
+
display: block;
|
154
|
+
}"""
|
155
|
+
|
156
|
+
|
157
|
+
## --- Notebook Creation and Update Functions ---
|
158
|
+
|
159
|
+
|
160
|
+
def create_base_notebook(messages: List[Dict[str, Any]]) -> Tuple[Dict[str, Any], int]:
|
161
|
+
"""
|
162
|
+
Create the base Jupyter Notebook structure with initial cells.
|
163
|
+
|
164
|
+
:param messages: List of conversation messages.
|
165
|
+
:return: A tuple of the notebook data dictionary and the current code cell counter.
|
166
|
+
"""
|
167
|
+
base_notebook = {
|
168
|
+
"metadata": {
|
169
|
+
"kernel_info": {"name": "python3"},
|
170
|
+
"language_info": {
|
171
|
+
"name": "python",
|
172
|
+
"version": "3.12",
|
173
|
+
},
|
174
|
+
},
|
175
|
+
"nbformat": 4,
|
176
|
+
"nbformat_minor": 0,
|
177
|
+
"cells": [],
|
178
|
+
}
|
179
|
+
base_notebook["cells"].append(
|
180
|
+
{"cell_type": "markdown", "metadata": {}, "source": header_message}
|
181
|
+
)
|
182
|
+
|
183
|
+
if len(messages) == 0:
|
184
|
+
base_notebook["cells"].append(
|
185
|
+
{
|
186
|
+
"cell_type": "code",
|
187
|
+
"execution_count": None,
|
188
|
+
"metadata": {},
|
189
|
+
"source": "",
|
190
|
+
"outputs": [],
|
191
|
+
}
|
192
|
+
)
|
193
|
+
|
194
|
+
code_cell_counter = 0
|
195
|
+
|
196
|
+
for message in messages:
|
197
|
+
if message["role"] == "system":
|
198
|
+
text = system_template.format(message["content"].replace("\n", "<br>"))
|
199
|
+
base_notebook["cells"].append(
|
200
|
+
{"cell_type": "markdown", "metadata": {}, "source": text}
|
201
|
+
)
|
202
|
+
elif message["role"] == "user":
|
203
|
+
text = user_template.format(message["content"].replace("\n", "<br>"))
|
204
|
+
base_notebook["cells"].append(
|
205
|
+
{"cell_type": "markdown", "metadata": {}, "source": text}
|
206
|
+
)
|
207
|
+
|
208
|
+
elif message["role"] == "assistant" and "tool_calls" in message:
|
209
|
+
base_notebook["cells"].append(
|
210
|
+
{
|
211
|
+
"cell_type": "code",
|
212
|
+
"execution_count": None,
|
213
|
+
"metadata": {},
|
214
|
+
"source": message["content"],
|
215
|
+
"outputs": [],
|
216
|
+
}
|
217
|
+
)
|
218
|
+
|
219
|
+
elif message["role"] == "ipython":
|
220
|
+
code_cell_counter += 1
|
221
|
+
base_notebook["cells"][-1]["outputs"] = message["nbformat"]
|
222
|
+
base_notebook["cells"][-1]["execution_count"] = code_cell_counter
|
223
|
+
|
224
|
+
elif message["role"] == "assistant" and "tool_calls" not in message:
|
225
|
+
base_notebook["cells"].append(
|
226
|
+
{"cell_type": "markdown", "metadata": {}, "source": message["content"]}
|
227
|
+
)
|
228
|
+
|
229
|
+
else:
|
230
|
+
raise ValueError(message)
|
231
|
+
|
232
|
+
return base_notebook, code_cell_counter
|
233
|
+
|
234
|
+
|
235
|
+
def execute_code(sbx: Sandbox, code: str) -> Tuple[str, Any]:
|
236
|
+
"""
|
237
|
+
Execute the given code in the provided sandbox.
|
238
|
+
|
239
|
+
:param sbx: Sandbox instance to run the code.
|
240
|
+
:param code: Code to execute.
|
241
|
+
:return: Tuple of aggregated output string and the raw execution object.
|
242
|
+
"""
|
243
|
+
execution = sbx.run_code(code, on_stdout=lambda data: print("stdout:", data))
|
244
|
+
output = ""
|
245
|
+
if len(execution.logs.stdout) > 0:
|
246
|
+
output += "\n".join(execution.logs.stdout)
|
247
|
+
if len(execution.logs.stderr) > 0:
|
248
|
+
output += "\n".join(execution.logs.stderr)
|
249
|
+
if execution.error is not None:
|
250
|
+
output += execution.error.traceback
|
251
|
+
return output, execution
|
252
|
+
|
253
|
+
|
254
|
+
def parse_exec_result_llm(execution: Any) -> str:
|
255
|
+
"""
|
256
|
+
Parse the execution results and return a single concatenated output string.
|
257
|
+
|
258
|
+
:param execution: Execution object from the sandbox.
|
259
|
+
:return: Concatenated string of output messages.
|
260
|
+
"""
|
261
|
+
output = ""
|
262
|
+
if len(execution.logs.stdout) > 0:
|
263
|
+
output += "\n".join(execution.logs.stdout)
|
264
|
+
if len(execution.logs.stderr) > 0:
|
265
|
+
output += "\n".join(execution.logs.stderr)
|
266
|
+
if execution.error is not None:
|
267
|
+
output += execution.error.traceback
|
268
|
+
return output
|
269
|
+
|
270
|
+
|
271
|
+
def update_notebook_display(notebook_data):
|
272
|
+
notebook = nbformat.from_dict(notebook_data)
|
273
|
+
notebook_body, _ = html_exporter.from_notebook_node(notebook)
|
274
|
+
notebook_body = notebook_body.replace(bad_html_bad, "")
|
275
|
+
return notebook_body
|
276
|
+
|
277
|
+
|
278
|
+
## --- Interactive Notebook Generation ---
|
279
|
+
|
280
|
+
|
281
|
+
def run_interactive_notebook(
|
282
|
+
client: InferenceClient,
|
283
|
+
model: str,
|
284
|
+
tokenizer: Any,
|
285
|
+
messages: List[Dict[str, Any]],
|
286
|
+
sbx: Sandbox,
|
287
|
+
max_new_tokens: int = 512,
|
288
|
+
) -> Any:
|
289
|
+
"""
|
290
|
+
Generator function that iteratively builds and updates the Jupyter Notebook.
|
291
|
+
|
292
|
+
:param client: Hugging Face InferenceClient for text generation.
|
293
|
+
:param model: Model identifier.
|
294
|
+
:param tokenizer: Tokenizer corresponding to the model.
|
295
|
+
:param messages: List of conversation messages.
|
296
|
+
:param sbx: Sandbox instance for executing code.
|
297
|
+
:param max_new_tokens: Maximum tokens to generate per turn.
|
298
|
+
:yield: Tuple containing updated notebook HTML, notebook data, and messages.
|
299
|
+
"""
|
300
|
+
notebook_data, code_cell_counter = create_base_notebook(messages)
|
301
|
+
turns = 0
|
302
|
+
|
303
|
+
# code_cell_counter = 0
|
304
|
+
while turns <= MAX_TURNS:
|
305
|
+
turns += 1
|
306
|
+
input_tokens = tokenizer.apply_chat_template(
|
307
|
+
messages,
|
308
|
+
chat_template=llama_template,
|
309
|
+
builtin_tools=["code_interpreter"],
|
310
|
+
add_generation_prompt=True,
|
311
|
+
)
|
312
|
+
model_input = tokenizer.decode(input_tokens)
|
313
|
+
|
314
|
+
print(f"Model input:\n{model_input}\n{'='*80}")
|
315
|
+
|
316
|
+
response_stream = client.text_generation(
|
317
|
+
model=model,
|
318
|
+
prompt=model_input,
|
319
|
+
details=True,
|
320
|
+
stream=True,
|
321
|
+
do_sample=True,
|
322
|
+
repetition_penalty=1.1,
|
323
|
+
temperature=0.8,
|
324
|
+
max_new_tokens=max_new_tokens,
|
325
|
+
)
|
326
|
+
|
327
|
+
assistant_response = ""
|
328
|
+
tokens = []
|
329
|
+
|
330
|
+
code_cell = False
|
331
|
+
for i, chunk in enumerate(response_stream):
|
332
|
+
if not chunk.token.special:
|
333
|
+
content = chunk.token.text
|
334
|
+
else:
|
335
|
+
content = ""
|
336
|
+
tokens.append(chunk.token.text)
|
337
|
+
assistant_response += content
|
338
|
+
|
339
|
+
if len(tokens) == 1:
|
340
|
+
create_cell = True
|
341
|
+
code_cell = "<|python_tag|>" in tokens[0]
|
342
|
+
if code_cell:
|
343
|
+
code_cell_counter += 1
|
344
|
+
else:
|
345
|
+
create_cell = False
|
346
|
+
|
347
|
+
## Update notebook cells in real-time
|
348
|
+
if create_cell:
|
349
|
+
if "<|python_tag|>" in tokens[0]:
|
350
|
+
notebook_data["cells"].append(
|
351
|
+
{
|
352
|
+
"cell_type": "code",
|
353
|
+
"execution_count": None,
|
354
|
+
"metadata": {},
|
355
|
+
"source": assistant_response,
|
356
|
+
"outputs": [],
|
357
|
+
}
|
358
|
+
)
|
359
|
+
else:
|
360
|
+
notebook_data["cells"].append(
|
361
|
+
{
|
362
|
+
"cell_type": "markdown",
|
363
|
+
"metadata": {},
|
364
|
+
"source": assistant_response,
|
365
|
+
}
|
366
|
+
)
|
367
|
+
else:
|
368
|
+
notebook_data["cells"][-1]["source"] = assistant_response
|
369
|
+
if i % 16 == 0:
|
370
|
+
yield update_notebook_display(notebook_data), notebook_data, messages
|
371
|
+
yield update_notebook_display(notebook_data), notebook_data, messages
|
372
|
+
|
373
|
+
## If a code cell was generated, execute the code
|
374
|
+
if code_cell:
|
375
|
+
notebook_data["cells"][-1]["execution_count"] = code_cell_counter
|
376
|
+
|
377
|
+
exec_result, execution = execute_code(sbx, assistant_response)
|
378
|
+
messages.append(
|
379
|
+
{
|
380
|
+
"role": "assistant",
|
381
|
+
"content": assistant_response,
|
382
|
+
"tool_calls": [
|
383
|
+
{
|
384
|
+
"type": "function",
|
385
|
+
"function": {
|
386
|
+
"name": "code_interpreter",
|
387
|
+
"arguments": {"code": assistant_response},
|
388
|
+
},
|
389
|
+
}
|
390
|
+
],
|
391
|
+
}
|
392
|
+
)
|
393
|
+
messages.append(
|
394
|
+
{
|
395
|
+
"role": "ipython",
|
396
|
+
"content": parse_exec_result_llm(execution),
|
397
|
+
"nbformat": parse_exec_result_nb(execution),
|
398
|
+
}
|
399
|
+
)
|
400
|
+
|
401
|
+
## Update the last code cell with execution results
|
402
|
+
notebook_data["cells"][-1]["outputs"] = parse_exec_result_nb(execution)
|
403
|
+
update_notebook_display(notebook_data)
|
404
|
+
else:
|
405
|
+
messages.append({"role": "assistant", "content": assistant_response})
|
406
|
+
if tokens[-1] == "<|eot_id|>":
|
407
|
+
break
|
408
|
+
|
409
|
+
yield update_notebook_display(notebook_data), notebook_data, messages
|
runbooks/__init__.py
CHANGED
@@ -1,7 +1,75 @@
|
|
1
1
|
"""
|
2
|
-
|
2
|
+
CloudOps Runbooks - Enterprise CloudOps Automation Toolkit
|
3
3
|
|
4
|
-
Provides
|
4
|
+
Provides comprehensive AWS automation capabilities including:
|
5
|
+
- Cloud Foundations Assessment Tool (CFAT)
|
6
|
+
- Multi-account resource inventory
|
7
|
+
- Organization management
|
8
|
+
- Control Tower automation
|
9
|
+
- Identity and access management
|
10
|
+
- Centralized logging setup
|
5
11
|
"""
|
6
12
|
|
7
|
-
|
13
|
+
from importlib.metadata import PackageNotFoundError
|
14
|
+
from importlib.metadata import version as _pkg_version
|
15
|
+
|
16
|
+
# Keep package version in sync with distribution metadata
|
17
|
+
try:
|
18
|
+
__version__ = _pkg_version("runbooks")
|
19
|
+
except Exception:
|
20
|
+
# Fallback if metadata is unavailable during editable installs
|
21
|
+
__version__ = "0.6.1"
|
22
|
+
|
23
|
+
# Core module exports
|
24
|
+
from runbooks.config import RunbooksConfig, load_config, save_config
|
25
|
+
from runbooks.utils import ensure_directory, setup_logging, validate_aws_profile
|
26
|
+
|
27
|
+
# Cloud Foundations exports - using direct structure
|
28
|
+
try:
|
29
|
+
from runbooks.cfat.runner import AssessmentRunner
|
30
|
+
from runbooks.inventory.core.collector import InventoryCollector
|
31
|
+
from runbooks.organizations.manager import OUManager
|
32
|
+
|
33
|
+
__all__ = [
|
34
|
+
"__version__",
|
35
|
+
"setup_logging",
|
36
|
+
"load_config",
|
37
|
+
"save_config",
|
38
|
+
"RunbooksConfig",
|
39
|
+
"AssessmentRunner",
|
40
|
+
"InventoryCollector",
|
41
|
+
"OUManager",
|
42
|
+
"ensure_directory",
|
43
|
+
"validate_aws_profile",
|
44
|
+
]
|
45
|
+
except ImportError as e:
|
46
|
+
# Graceful degradation if dependencies aren't available
|
47
|
+
__all__ = [
|
48
|
+
"__version__",
|
49
|
+
"setup_logging",
|
50
|
+
"load_config",
|
51
|
+
"save_config",
|
52
|
+
"RunbooksConfig",
|
53
|
+
"ensure_directory",
|
54
|
+
"validate_aws_profile",
|
55
|
+
]
|
56
|
+
|
57
|
+
# FinOps exports
|
58
|
+
from runbooks.finops import get_cost_data, get_trend, run_dashboard
|
59
|
+
|
60
|
+
__all__ = [
|
61
|
+
"__version__",
|
62
|
+
"setup_logging",
|
63
|
+
"load_config",
|
64
|
+
"save_config",
|
65
|
+
"RunbooksConfig",
|
66
|
+
"AssessmentRunner",
|
67
|
+
"InventoryCollector",
|
68
|
+
"OUManager",
|
69
|
+
"ensure_directory",
|
70
|
+
"validate_aws_profile",
|
71
|
+
# FinOps
|
72
|
+
"run_dashboard",
|
73
|
+
"get_cost_data",
|
74
|
+
"get_trend",
|
75
|
+
]
|
runbooks/__main__.py
ADDED
@@ -125,7 +125,7 @@ def display_instances_json(instances: List[Dict[str, str]]) -> None:
|
|
125
125
|
Args:
|
126
126
|
instances (List[Dict[str, str]]): List of EC2 instance details.
|
127
127
|
"""
|
128
|
-
print(json.dumps(instances, indent=4)) if instances else print("No instances found.")
|
128
|
+
(print(json.dumps(instances, indent=4)) if instances else print("No instances found."))
|
129
129
|
|
130
130
|
|
131
131
|
# ==============================
|
@@ -135,7 +135,10 @@ def launch_ec2_instances(
|
|
135
135
|
|
136
136
|
## ✅ Apply Tags
|
137
137
|
if tags:
|
138
|
-
ec2_client.create_tags(
|
138
|
+
ec2_client.create_tags(
|
139
|
+
Resources=instance_ids,
|
140
|
+
Tags=[{"Key": k, "Value": v} for k, v in tags.items()],
|
141
|
+
)
|
139
142
|
logger.info(f"Applied tags: {tags}")
|
140
143
|
|
141
144
|
return instance_ids
|
@@ -182,7 +185,10 @@ def lambda_handler(event, context):
|
|
182
185
|
)
|
183
186
|
|
184
187
|
## ✅ Return Success Response
|
185
|
-
return {
|
188
|
+
return {
|
189
|
+
"statusCode": 200,
|
190
|
+
"body": json.dumps({"message": "Instances launched", "InstanceIDs": instance_ids}),
|
191
|
+
}
|
186
192
|
except Exception as e:
|
187
193
|
logger.error(f"Lambda Handler Error: {e}")
|
188
194
|
return {"statusCode": 500, "body": json.dumps({"error": str(e)})}
|
@@ -27,14 +27,19 @@ Usage (Lambda):
|
|
27
27
|
Trigger event with: {"action": "start"} or {"action": "stop"}
|
28
28
|
"""
|
29
29
|
|
30
|
-
import argparse
|
30
|
+
import argparse # # For CLI mode support
|
31
31
|
import json
|
32
32
|
import os
|
33
33
|
import sys
|
34
34
|
from typing import Dict, List
|
35
35
|
|
36
36
|
import boto3
|
37
|
-
from botocore.exceptions import
|
37
|
+
from botocore.exceptions import (
|
38
|
+
BotoCoreError,
|
39
|
+
ClientError,
|
40
|
+
NoCredentialsError,
|
41
|
+
PartialCredentialsError,
|
42
|
+
)
|
38
43
|
|
39
44
|
from runbooks.utils.logger import configure_logger
|
40
45
|
|
@@ -161,7 +166,10 @@ def lambda_handler(event, context):
|
|
161
166
|
instance_ids = fetch_instances(ec2_client, tag_key="AutoStart", tag_value="True")
|
162
167
|
perform_action(ec2_client, instance_ids, action)
|
163
168
|
|
164
|
-
return {
|
169
|
+
return {
|
170
|
+
"statusCode": 200,
|
171
|
+
"body": json.dumps(f"Action '{action}' completed successfully."),
|
172
|
+
}
|
165
173
|
|
166
174
|
except Exception as e:
|
167
175
|
logger.error(f"Error: {e}")
|
@@ -173,7 +181,12 @@ def main():
|
|
173
181
|
CLI Entry Point for Python Usage.
|
174
182
|
"""
|
175
183
|
parser = argparse.ArgumentParser(description="EC2 Scheduler Script")
|
176
|
-
parser.add_argument(
|
184
|
+
parser.add_argument(
|
185
|
+
"--action",
|
186
|
+
choices=["start", "stop"],
|
187
|
+
required=True,
|
188
|
+
help="Action to perform (start/stop).",
|
189
|
+
)
|
177
190
|
args = parser.parse_args()
|
178
191
|
|
179
192
|
try:
|
@@ -126,7 +126,11 @@ def send_sns_notification(unused_volumes: List[Dict[str, str]]) -> None:
|
|
126
126
|
## ✅ Publish to SNS
|
127
127
|
logger.info(f"Sending notification to SNS topic: {SNS_TOPIC_ARN}...")
|
128
128
|
logger.info(f"📤 Sending SNS notification to SNS topic: {SNS_TOPIC_ARN}")
|
129
|
-
sns_client.publish(
|
129
|
+
sns_client.publish(
|
130
|
+
TopicArn=SNS_TOPIC_ARN,
|
131
|
+
Subject="Unused EBS Volumes Report",
|
132
|
+
Message=email_body,
|
133
|
+
)
|
130
134
|
logger.info("✅ SNS notification sent successfully.")
|
131
135
|
|
132
136
|
except ClientError as e:
|
runbooks/aws/s3_create_bucket.py
CHANGED
@@ -14,7 +14,7 @@ from typing import Optional
|
|
14
14
|
import boto3
|
15
15
|
from botocore.exceptions import BotoCoreError, ClientError
|
16
16
|
|
17
|
-
from runbooks.utils.logger import configure_logger
|
17
|
+
from runbooks.utils.logger import configure_logger # # Import reusable logger
|
18
18
|
|
19
19
|
## Initialize Logger
|
20
20
|
logger = configure_logger("list_s3_buckets")
|
@@ -87,7 +87,9 @@ def create_s3_bucket(bucket_name: str, region: str) -> Optional[str]:
|
|
87
87
|
)
|
88
88
|
else:
|
89
89
|
response = s3_client.create_bucket(
|
90
|
-
Bucket=bucket_name,
|
90
|
+
Bucket=bucket_name,
|
91
|
+
ACL="private",
|
92
|
+
CreateBucketConfiguration={"LocationConstraint": region},
|
91
93
|
)
|
92
94
|
|
93
95
|
logger.info(f"✅ Bucket '{bucket_name}' created successfully at {response['Location']}.")
|
runbooks/aws/s3_list_objects.py
CHANGED
@@ -129,7 +129,12 @@ def main():
|
|
129
129
|
parser = argparse.ArgumentParser(description="List objects in an AWS S3 bucket.")
|
130
130
|
parser.add_argument("--bucket", required=True, help="The name of the S3 bucket.")
|
131
131
|
parser.add_argument("--prefix", default=None, help="Filter objects by prefix.")
|
132
|
-
parser.add_argument(
|
132
|
+
parser.add_argument(
|
133
|
+
"--max-keys",
|
134
|
+
type=int,
|
135
|
+
default=1000,
|
136
|
+
help="Max number of keys to fetch (default: 1000).",
|
137
|
+
)
|
133
138
|
|
134
139
|
args = parser.parse_args()
|
135
140
|
|
@@ -48,7 +48,15 @@ LOCAL_FILE_PATH = "/tmp/tags.json" ## Local Temp file path
|
|
48
48
|
# ==============================
|
49
49
|
# VALIDATION CONFIGURATIONS
|
50
50
|
# ==============================
|
51
|
-
REQUIRED_TAGS = [
|
51
|
+
REQUIRED_TAGS = [
|
52
|
+
"Account Name",
|
53
|
+
"Functional Area",
|
54
|
+
"WBS Code",
|
55
|
+
"Business Unit",
|
56
|
+
"Managed by",
|
57
|
+
"CostGroup",
|
58
|
+
"TechOwner",
|
59
|
+
]
|
52
60
|
|
53
61
|
TAG_VALUE_REGEX = r"^[a-zA-Z0-9\s\-_@]+$" ## Allowed characters for tag values
|
54
62
|
|
@@ -166,7 +174,10 @@ def lambda_handler(event, context):
|
|
166
174
|
apply_tags_to_instance(instance_id, tags)
|
167
175
|
|
168
176
|
## ✅ Success Response
|
169
|
-
return {
|
177
|
+
return {
|
178
|
+
"statusCode": 200,
|
179
|
+
"body": json.dumps(f"Tags successfully applied to instance {instance_id}"),
|
180
|
+
}
|
170
181
|
except Exception as e:
|
171
182
|
logger.error(f"Error during tagging process: {e}")
|
172
183
|
return {"statusCode": 500, "body": json.dumps(f"Error: {str(e)}")}
|
runbooks/aws/tags.json
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
[
|
2
|
+
{"Key": "Environment", "Value": "Production"},
|
3
|
+
{"Key": "Project", "Value": "JHCApp"},
|
4
|
+
{"Key": "CostCenter", "Value": "JHC007"},
|
5
|
+
{"Key": "Account Name", "Value": "OceanSoft Platform"},
|
6
|
+
{"Key": "Functional Area", "Value": "Shared Services"},
|
7
|
+
{"Key": "WBS Code", "Value": "OS12345"},
|
8
|
+
{"Key": "Business Unit", "Value": "Platform"},
|
9
|
+
{"Key": "Managed by", "Value": "DevOps Team"},
|
10
|
+
{"Key": "CostGroup", "Value": "CloudOps"},
|
11
|
+
{"Key": "TechOwner", "Value": "tech@oceansoft.io"}
|
12
|
+
]
|