runbooks 0.7.0__py3-none-any.whl → 0.7.6__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 +87 -37
- runbooks/cfat/README.md +300 -49
- runbooks/cfat/__init__.py +2 -2
- runbooks/finops/__init__.py +1 -1
- runbooks/finops/cli.py +1 -1
- runbooks/inventory/collectors/__init__.py +8 -0
- runbooks/inventory/collectors/aws_management.py +791 -0
- runbooks/inventory/collectors/aws_networking.py +3 -3
- runbooks/main.py +3389 -782
- runbooks/operate/__init__.py +207 -0
- runbooks/operate/base.py +311 -0
- runbooks/operate/cloudformation_operations.py +619 -0
- runbooks/operate/cloudwatch_operations.py +496 -0
- runbooks/operate/dynamodb_operations.py +812 -0
- runbooks/operate/ec2_operations.py +926 -0
- runbooks/operate/iam_operations.py +569 -0
- runbooks/operate/s3_operations.py +1211 -0
- runbooks/operate/tagging_operations.py +655 -0
- runbooks/remediation/CLAUDE.md +100 -0
- runbooks/remediation/DOME9.md +218 -0
- runbooks/remediation/README.md +26 -0
- runbooks/remediation/Tests/__init__.py +0 -0
- runbooks/remediation/Tests/update_policy.py +74 -0
- runbooks/remediation/__init__.py +95 -0
- runbooks/remediation/acm_cert_expired_unused.py +98 -0
- runbooks/remediation/acm_remediation.py +875 -0
- runbooks/remediation/api_gateway_list.py +167 -0
- runbooks/remediation/base.py +643 -0
- runbooks/remediation/cloudtrail_remediation.py +908 -0
- runbooks/remediation/cloudtrail_s3_modifications.py +296 -0
- runbooks/remediation/cognito_active_users.py +78 -0
- runbooks/remediation/cognito_remediation.py +856 -0
- runbooks/remediation/cognito_user_password_reset.py +163 -0
- runbooks/remediation/commons.py +455 -0
- runbooks/remediation/dynamodb_optimize.py +155 -0
- runbooks/remediation/dynamodb_remediation.py +744 -0
- runbooks/remediation/dynamodb_server_side_encryption.py +108 -0
- runbooks/remediation/ec2_public_ips.py +134 -0
- runbooks/remediation/ec2_remediation.py +892 -0
- runbooks/remediation/ec2_subnet_disable_auto_ip_assignment.py +72 -0
- runbooks/remediation/ec2_unattached_ebs_volumes.py +448 -0
- runbooks/remediation/ec2_unused_security_groups.py +202 -0
- runbooks/remediation/kms_enable_key_rotation.py +651 -0
- runbooks/remediation/kms_remediation.py +717 -0
- runbooks/remediation/lambda_list.py +243 -0
- runbooks/remediation/lambda_remediation.py +971 -0
- runbooks/remediation/multi_account.py +569 -0
- runbooks/remediation/rds_instance_list.py +199 -0
- runbooks/remediation/rds_remediation.py +873 -0
- runbooks/remediation/rds_snapshot_list.py +192 -0
- runbooks/remediation/requirements.txt +118 -0
- runbooks/remediation/s3_block_public_access.py +159 -0
- runbooks/remediation/s3_bucket_public_access.py +143 -0
- runbooks/remediation/s3_disable_static_website_hosting.py +74 -0
- runbooks/remediation/s3_downloader.py +215 -0
- runbooks/remediation/s3_enable_access_logging.py +562 -0
- runbooks/remediation/s3_encryption.py +526 -0
- runbooks/remediation/s3_force_ssl_secure_policy.py +143 -0
- runbooks/remediation/s3_list.py +141 -0
- runbooks/remediation/s3_object_search.py +201 -0
- runbooks/remediation/s3_remediation.py +816 -0
- runbooks/remediation/scan_for_phrase.py +425 -0
- runbooks/remediation/workspaces_list.py +220 -0
- runbooks/security/__init__.py +9 -10
- runbooks/security/security_baseline_tester.py +4 -2
- runbooks-0.7.6.dist-info/METADATA +608 -0
- {runbooks-0.7.0.dist-info → runbooks-0.7.6.dist-info}/RECORD +84 -76
- {runbooks-0.7.0.dist-info → runbooks-0.7.6.dist-info}/entry_points.txt +0 -1
- {runbooks-0.7.0.dist-info → runbooks-0.7.6.dist-info}/top_level.txt +0 -1
- jupyter-agent/.env +0 -2
- jupyter-agent/.env.template +0 -2
- jupyter-agent/.gitattributes +0 -35
- jupyter-agent/.gradio/certificate.pem +0 -31
- jupyter-agent/README.md +0 -16
- jupyter-agent/__main__.log +0 -8
- jupyter-agent/app.py +0 -256
- jupyter-agent/cloudops-agent.png +0 -0
- jupyter-agent/ds-system-prompt.txt +0 -154
- jupyter-agent/jupyter-agent.png +0 -0
- jupyter-agent/llama3_template.jinja +0 -123
- jupyter-agent/requirements.txt +0 -9
- jupyter-agent/tmp/4ojbs8a02ir/jupyter-agent.ipynb +0 -68
- jupyter-agent/tmp/cm5iasgpm3p/jupyter-agent.ipynb +0 -91
- jupyter-agent/tmp/crqbsseag5/jupyter-agent.ipynb +0 -91
- jupyter-agent/tmp/hohanq1u097/jupyter-agent.ipynb +0 -57
- jupyter-agent/tmp/jns1sam29wm/jupyter-agent.ipynb +0 -53
- jupyter-agent/tmp/jupyter-agent.ipynb +0 -27
- jupyter-agent/utils.py +0 -409
- runbooks/aws/__init__.py +0 -58
- runbooks/aws/dynamodb_operations.py +0 -231
- runbooks/aws/ec2_copy_image_cross-region.py +0 -195
- runbooks/aws/ec2_describe_instances.py +0 -202
- runbooks/aws/ec2_ebs_snapshots_delete.py +0 -186
- runbooks/aws/ec2_run_instances.py +0 -213
- runbooks/aws/ec2_start_stop_instances.py +0 -212
- runbooks/aws/ec2_terminate_instances.py +0 -143
- runbooks/aws/ec2_unused_eips.py +0 -196
- runbooks/aws/ec2_unused_volumes.py +0 -188
- runbooks/aws/s3_create_bucket.py +0 -142
- runbooks/aws/s3_list_buckets.py +0 -152
- runbooks/aws/s3_list_objects.py +0 -156
- runbooks/aws/s3_object_operations.py +0 -183
- runbooks/aws/tagging_lambda_handler.py +0 -183
- runbooks/inventory/FAILED_SCRIPTS_TROUBLESHOOTING.md +0 -619
- runbooks/inventory/PASSED_SCRIPTS_GUIDE.md +0 -738
- runbooks/inventory/aws_organization.png +0 -0
- runbooks/inventory/cfn_move_stack_instances.py +0 -1526
- runbooks/inventory/delete_s3_buckets_objects.py +0 -169
- runbooks/inventory/lockdown_cfn_stackset_role.py +0 -224
- runbooks/inventory/update_aws_actions.py +0 -173
- runbooks/inventory/update_cfn_stacksets.py +0 -1215
- runbooks/inventory/update_cloudwatch_logs_retention_policy.py +0 -294
- runbooks/inventory/update_iam_roles_cross_accounts.py +0 -478
- runbooks/inventory/update_s3_public_access_block.py +0 -539
- runbooks/organizations/__init__.py +0 -12
- runbooks/organizations/manager.py +0 -374
- runbooks-0.7.0.dist-info/METADATA +0 -375
- /runbooks/inventory/{tests → Tests}/common_test_data.py +0 -0
- /runbooks/inventory/{tests → Tests}/common_test_functions.py +0 -0
- /runbooks/inventory/{tests → Tests}/script_test_data.py +0 -0
- /runbooks/inventory/{tests → Tests}/setup.py +0 -0
- /runbooks/inventory/{tests → Tests}/src.py +0 -0
- /runbooks/inventory/{tests/test_inventory_modules.py → Tests/test_Inventory_Modules.py} +0 -0
- /runbooks/inventory/{tests → Tests}/test_cfn_describe_stacks.py +0 -0
- /runbooks/inventory/{tests → Tests}/test_ec2_describe_instances.py +0 -0
- /runbooks/inventory/{tests → Tests}/test_lambda_list_functions.py +0 -0
- /runbooks/inventory/{tests → Tests}/test_moto_integration_example.py +0 -0
- /runbooks/inventory/{tests → Tests}/test_org_list_accounts.py +0 -0
- /runbooks/inventory/{Inventory_Modules.py → inventory_modules.py} +0 -0
- /runbooks/{aws → operate}/tags.json +0 -0
- {runbooks-0.7.0.dist-info → runbooks-0.7.6.dist-info}/WHEEL +0 -0
- {runbooks-0.7.0.dist-info → runbooks-0.7.6.dist-info}/licenses/LICENSE +0 -0
jupyter-agent/utils.py
DELETED
@@ -1,409 +0,0 @@
|
|
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/aws/__init__.py
DELETED
@@ -1,58 +0,0 @@
|
|
1
|
-
## src/runbooks/aws/__init__.py
|
2
|
-
"""AWS Runbooks Initialization Module."""
|
3
|
-
|
4
|
-
import importlib
|
5
|
-
import os
|
6
|
-
import sys
|
7
|
-
|
8
|
-
from runbooks.utils.logger import configure_logger
|
9
|
-
|
10
|
-
logger = configure_logger(__name__)
|
11
|
-
|
12
|
-
|
13
|
-
def discover_scripts():
|
14
|
-
"""
|
15
|
-
Dynamically discovers and lists all AWS scripts in this package.
|
16
|
-
|
17
|
-
Returns:
|
18
|
-
dict: A mapping of script names to their main functions.
|
19
|
-
"""
|
20
|
-
scripts = {}
|
21
|
-
aws_path = os.path.dirname(__file__)
|
22
|
-
for filename in os.listdir(aws_path):
|
23
|
-
if filename.endswith(".py") and filename != "__init__.py":
|
24
|
-
module_name = f"runbooks.aws.{filename[:-3]}"
|
25
|
-
try:
|
26
|
-
module = importlib.import_module(module_name)
|
27
|
-
if hasattr(module, "main"):
|
28
|
-
scripts[filename[:-3]] = module.main
|
29
|
-
except Exception as e:
|
30
|
-
logger.error(f"Error importing {module_name}: {e}")
|
31
|
-
return scripts
|
32
|
-
|
33
|
-
|
34
|
-
def run_script(script_name, *args):
|
35
|
-
"""
|
36
|
-
Executes the given script by name.
|
37
|
-
|
38
|
-
Args:
|
39
|
-
script_name (str): The name of the script to execute.
|
40
|
-
*args: Additional arguments to pass to the script.
|
41
|
-
"""
|
42
|
-
scripts = discover_scripts()
|
43
|
-
if script_name in scripts:
|
44
|
-
try:
|
45
|
-
scripts[script_name](*args)
|
46
|
-
except Exception as e:
|
47
|
-
logger.error(f"Error executing script {script_name}: {e}")
|
48
|
-
else:
|
49
|
-
logger.error(f"Script {script_name} not found.")
|
50
|
-
sys.exit(1)
|
51
|
-
|
52
|
-
|
53
|
-
if __name__ == "__main__":
|
54
|
-
if len(sys.argv) < 2:
|
55
|
-
logger.error("Usage: python -m runbooks.aws <script_name> [<args>]")
|
56
|
-
sys.exit(1)
|
57
|
-
|
58
|
-
run_script(sys.argv[1], *sys.argv[2:])
|
@@ -1,231 +0,0 @@
|
|
1
|
-
#!/usr/bin/env python3
|
2
|
-
|
3
|
-
"""
|
4
|
-
DynamoDB Operations: Put Item, Delete Item, and Batch Write.
|
5
|
-
|
6
|
-
This script supports the following functionalities:
|
7
|
-
1. Insert or update a single item (Put Item).
|
8
|
-
2. Retrieve and delete a single item (Delete Item).
|
9
|
-
3. Batch insert multiple items efficiently (Batch Write).
|
10
|
-
|
11
|
-
Designed for usage in Python, Docker, and AWS Lambda environments.
|
12
|
-
|
13
|
-
Author: nnthanh101@gmail.com
|
14
|
-
Date: 2025-01-09
|
15
|
-
Version: 1.0.0
|
16
|
-
"""
|
17
|
-
|
18
|
-
import json
|
19
|
-
import os
|
20
|
-
from typing import Dict, List
|
21
|
-
|
22
|
-
import boto3
|
23
|
-
from botocore.exceptions import BotoCoreError, ClientError
|
24
|
-
|
25
|
-
from runbooks.utils.logger import configure_logger
|
26
|
-
|
27
|
-
## ✅ Configure Logger
|
28
|
-
logger = configure_logger(__name__)
|
29
|
-
|
30
|
-
# ==============================
|
31
|
-
# CONFIGURATION VARIABLES
|
32
|
-
# ==============================
|
33
|
-
AWS_REGION = os.getenv("AWS_REGION", "us-east-1")
|
34
|
-
TABLE_NAME = os.getenv("TABLE_NAME", "employees")
|
35
|
-
MAX_BATCH_ITEMS = int(os.getenv("MAX_BATCH_ITEMS", 100))
|
36
|
-
|
37
|
-
|
38
|
-
# ==============================
|
39
|
-
# AWS CLIENT INITIALIZATION
|
40
|
-
# ==============================
|
41
|
-
try:
|
42
|
-
dynamodb = boto3.resource("dynamodb", region_name=AWS_REGION)
|
43
|
-
table = dynamodb.Table(TABLE_NAME)
|
44
|
-
logger.info(f"✅ DynamoDB Table '{TABLE_NAME}' initialized successfully.")
|
45
|
-
except Exception as e:
|
46
|
-
logger.error(f"❌ Failed to initialize DynamoDB table: {e}")
|
47
|
-
raise
|
48
|
-
|
49
|
-
|
50
|
-
# ==============================
|
51
|
-
# FUNCTION: PUT ITEM
|
52
|
-
# ==============================
|
53
|
-
def put_item(emp_id: str, name: str, salary: int) -> None:
|
54
|
-
"""
|
55
|
-
Inserts or updates a single item in DynamoDB.
|
56
|
-
|
57
|
-
Args:
|
58
|
-
emp_id (str): Employee ID.
|
59
|
-
name (str): Employee name.
|
60
|
-
salary (int): Employee salary.
|
61
|
-
|
62
|
-
Raises:
|
63
|
-
Exception: If item insertion fails.
|
64
|
-
"""
|
65
|
-
try:
|
66
|
-
logger.info(f"🚀 Inserting/Updating item in table '{TABLE_NAME}'...")
|
67
|
-
table.put_item(Item={"emp_id": emp_id, "name": name, "salary": salary})
|
68
|
-
logger.info(f"✅ Item added successfully: emp_id={emp_id}, name={name}, salary={salary}")
|
69
|
-
|
70
|
-
except ClientError as e:
|
71
|
-
logger.error(f"❌ AWS Client Error: {e}")
|
72
|
-
raise
|
73
|
-
|
74
|
-
except Exception as e:
|
75
|
-
logger.error(f"❌ Unexpected Error: {e}")
|
76
|
-
raise
|
77
|
-
|
78
|
-
|
79
|
-
# ==============================
|
80
|
-
# FUNCTION: DELETE ITEM
|
81
|
-
# ==============================
|
82
|
-
def delete_item(emp_id: str) -> Dict:
|
83
|
-
"""
|
84
|
-
Retrieves and deletes a single item from DynamoDB.
|
85
|
-
|
86
|
-
Args:
|
87
|
-
emp_id (str): Employee ID.
|
88
|
-
|
89
|
-
Returns:
|
90
|
-
Dict: Deleted item details.
|
91
|
-
|
92
|
-
Raises:
|
93
|
-
Exception: If retrieval or deletion fails.
|
94
|
-
"""
|
95
|
-
try:
|
96
|
-
## ✅ 1. Retrieve the item
|
97
|
-
logger.info(f"🔍 Retrieving item with emp_id={emp_id}...")
|
98
|
-
response = table.get_item(Key={"emp_id": emp_id})
|
99
|
-
|
100
|
-
if "Item" not in response:
|
101
|
-
raise ValueError(f"Item with emp_id={emp_id} not found.")
|
102
|
-
item = response["Item"]
|
103
|
-
logger.info(f"✅ Item retrieved: {item}")
|
104
|
-
|
105
|
-
## ✅ 2. Delete the item
|
106
|
-
logger.info(f"🗑️ Deleting item with emp_id={emp_id}...")
|
107
|
-
table.delete_item(Key={"emp_id": emp_id})
|
108
|
-
logger.info(f"✅ Item deleted successfully: emp_id={emp_id}")
|
109
|
-
|
110
|
-
return item
|
111
|
-
|
112
|
-
except ClientError as e:
|
113
|
-
logger.error(f"❌ AWS Client Error: {e}")
|
114
|
-
raise
|
115
|
-
|
116
|
-
except BotoCoreError as e:
|
117
|
-
logger.error(f"❌ BotoCore Error: {e}")
|
118
|
-
raise
|
119
|
-
|
120
|
-
except Exception as e:
|
121
|
-
logger.error(f"❌ Unexpected Error: {e}")
|
122
|
-
raise
|
123
|
-
|
124
|
-
|
125
|
-
# ==============================
|
126
|
-
# FUNCTION: BATCH WRITE ITEMS
|
127
|
-
# ==============================
|
128
|
-
def batch_write_items(batch_size: int = MAX_BATCH_ITEMS) -> None:
|
129
|
-
"""
|
130
|
-
Inserts multiple items into DynamoDB using batch writer.
|
131
|
-
|
132
|
-
Args:
|
133
|
-
batch_size (int): Number of items to write in a batch.
|
134
|
-
|
135
|
-
Raises:
|
136
|
-
Exception: If batch write fails.
|
137
|
-
"""
|
138
|
-
try:
|
139
|
-
logger.info(f"🚀 Starting batch write with {batch_size} items...")
|
140
|
-
with table.batch_writer() as batch:
|
141
|
-
for i in range(batch_size):
|
142
|
-
batch.put_item(
|
143
|
-
Item={
|
144
|
-
"emp_id": str(i),
|
145
|
-
"name": f"Name-{i}",
|
146
|
-
"salary": 50000 + i * 100, ## Incremental salary
|
147
|
-
}
|
148
|
-
)
|
149
|
-
logger.info(f"✅ Batch write completed successfully with {batch_size} items.")
|
150
|
-
|
151
|
-
except ClientError as e:
|
152
|
-
logger.error(f"❌ AWS Client Error: {e}")
|
153
|
-
raise
|
154
|
-
|
155
|
-
except BotoCoreError as e:
|
156
|
-
logger.error(f"❌ BotoCore Error: {e}")
|
157
|
-
raise
|
158
|
-
|
159
|
-
except Exception as e:
|
160
|
-
logger.error(f"❌ Unexpected Error: {e}")
|
161
|
-
raise
|
162
|
-
|
163
|
-
|
164
|
-
# ==============================
|
165
|
-
# MAIN FUNCTION (CLI/DOCKER)
|
166
|
-
# ==============================
|
167
|
-
def main():
|
168
|
-
"""
|
169
|
-
Main function for CLI/Docker execution.
|
170
|
-
"""
|
171
|
-
try:
|
172
|
-
## Use-Case 1: Put Item
|
173
|
-
put_item(emp_id="2", name="John Doe", salary=75000)
|
174
|
-
|
175
|
-
## Use-Case 2: Delete Item
|
176
|
-
delete_item(emp_id="2")
|
177
|
-
|
178
|
-
## Use-Case 3: Batch Write Items
|
179
|
-
batch_write_items(batch_size=MAX_BATCH_ITEMS)
|
180
|
-
|
181
|
-
except Exception as e:
|
182
|
-
logger.error(f"❌ Error in main execution: {e}")
|
183
|
-
raise
|
184
|
-
|
185
|
-
|
186
|
-
# ==============================
|
187
|
-
# AWS LAMBDA HANDLER
|
188
|
-
# ==============================
|
189
|
-
def lambda_handler(event, context):
|
190
|
-
"""
|
191
|
-
AWS Lambda handler for DynamoDB operations.
|
192
|
-
|
193
|
-
Args:
|
194
|
-
event (dict): AWS Lambda event with action details.
|
195
|
-
context: AWS Lambda context object.
|
196
|
-
|
197
|
-
Returns:
|
198
|
-
dict: Status code and message.
|
199
|
-
"""
|
200
|
-
try:
|
201
|
-
action = event.get("action")
|
202
|
-
emp_id = event.get("emp_id")
|
203
|
-
name = event.get("name")
|
204
|
-
salary = event.get("salary", 0)
|
205
|
-
batch_size = int(event.get("batch_size", MAX_BATCH_ITEMS))
|
206
|
-
|
207
|
-
if action == "put":
|
208
|
-
put_item(emp_id, name, salary)
|
209
|
-
return {"statusCode": 200, "body": f"Item {emp_id} inserted."}
|
210
|
-
|
211
|
-
elif action == "delete":
|
212
|
-
item = delete_item(emp_id)
|
213
|
-
return {"statusCode": 200, "body": f"Item {item} deleted."}
|
214
|
-
|
215
|
-
elif action == "batch_write":
|
216
|
-
batch_write_items(batch_size)
|
217
|
-
return {"statusCode": 200, "body": "Batch write completed."}
|
218
|
-
|
219
|
-
else:
|
220
|
-
raise ValueError("Invalid action. Use 'put', 'delete', or 'batch_write'.")
|
221
|
-
|
222
|
-
except Exception as e:
|
223
|
-
logger.error(f"❌ Lambda Error: {e}")
|
224
|
-
return {"statusCode": 500, "body": str(e)}
|
225
|
-
|
226
|
-
|
227
|
-
# ==============================
|
228
|
-
# SCRIPT ENTRY POINT
|
229
|
-
# ==============================
|
230
|
-
if __name__ == "__main__":
|
231
|
-
main()
|