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.
Files changed (221) hide show
  1. conftest.py +26 -0
  2. jupyter-agent/.env.template +2 -0
  3. jupyter-agent/.gitattributes +35 -0
  4. jupyter-agent/README.md +16 -0
  5. jupyter-agent/app.py +256 -0
  6. jupyter-agent/cloudops-agent.png +0 -0
  7. jupyter-agent/ds-system-prompt.txt +154 -0
  8. jupyter-agent/jupyter-agent.png +0 -0
  9. jupyter-agent/llama3_template.jinja +123 -0
  10. jupyter-agent/requirements.txt +9 -0
  11. jupyter-agent/utils.py +409 -0
  12. runbooks/__init__.py +71 -3
  13. runbooks/__main__.py +13 -0
  14. runbooks/aws/ec2_describe_instances.py +1 -1
  15. runbooks/aws/ec2_run_instances.py +8 -2
  16. runbooks/aws/ec2_start_stop_instances.py +17 -4
  17. runbooks/aws/ec2_unused_volumes.py +5 -1
  18. runbooks/aws/s3_create_bucket.py +4 -2
  19. runbooks/aws/s3_list_objects.py +6 -1
  20. runbooks/aws/tagging_lambda_handler.py +13 -2
  21. runbooks/aws/tags.json +12 -0
  22. runbooks/base.py +353 -0
  23. runbooks/cfat/README.md +49 -0
  24. runbooks/cfat/__init__.py +74 -0
  25. runbooks/cfat/app.ts +644 -0
  26. runbooks/cfat/assessment/__init__.py +40 -0
  27. runbooks/cfat/assessment/asana-import.csv +39 -0
  28. runbooks/cfat/assessment/cfat-checks.csv +31 -0
  29. runbooks/cfat/assessment/cfat.txt +520 -0
  30. runbooks/cfat/assessment/collectors.py +200 -0
  31. runbooks/cfat/assessment/jira-import.csv +39 -0
  32. runbooks/cfat/assessment/runner.py +387 -0
  33. runbooks/cfat/assessment/validators.py +290 -0
  34. runbooks/cfat/cli.py +103 -0
  35. runbooks/cfat/docs/asana-import.csv +24 -0
  36. runbooks/cfat/docs/cfat-checks.csv +31 -0
  37. runbooks/cfat/docs/cfat.txt +335 -0
  38. runbooks/cfat/docs/checks-output.png +0 -0
  39. runbooks/cfat/docs/cloudshell-console-run.png +0 -0
  40. runbooks/cfat/docs/cloudshell-download.png +0 -0
  41. runbooks/cfat/docs/cloudshell-output.png +0 -0
  42. runbooks/cfat/docs/downloadfile.png +0 -0
  43. runbooks/cfat/docs/jira-import.csv +24 -0
  44. runbooks/cfat/docs/open-cloudshell.png +0 -0
  45. runbooks/cfat/docs/report-header.png +0 -0
  46. runbooks/cfat/models.py +1026 -0
  47. runbooks/cfat/package-lock.json +5116 -0
  48. runbooks/cfat/package.json +38 -0
  49. runbooks/cfat/report.py +496 -0
  50. runbooks/cfat/reporting/__init__.py +46 -0
  51. runbooks/cfat/reporting/exporters.py +337 -0
  52. runbooks/cfat/reporting/formatters.py +496 -0
  53. runbooks/cfat/reporting/templates.py +135 -0
  54. runbooks/cfat/run-assessment.sh +23 -0
  55. runbooks/cfat/runner.py +69 -0
  56. runbooks/cfat/src/actions/check-cloudtrail-existence.ts +43 -0
  57. runbooks/cfat/src/actions/check-config-existence.ts +37 -0
  58. runbooks/cfat/src/actions/check-control-tower.ts +37 -0
  59. runbooks/cfat/src/actions/check-ec2-existence.ts +46 -0
  60. runbooks/cfat/src/actions/check-iam-users.ts +50 -0
  61. runbooks/cfat/src/actions/check-legacy-cur.ts +30 -0
  62. runbooks/cfat/src/actions/check-org-cloudformation.ts +30 -0
  63. runbooks/cfat/src/actions/check-vpc-existence.ts +43 -0
  64. runbooks/cfat/src/actions/create-asanaimport.ts +14 -0
  65. runbooks/cfat/src/actions/create-backlog.ts +372 -0
  66. runbooks/cfat/src/actions/create-jiraimport.ts +15 -0
  67. runbooks/cfat/src/actions/create-report.ts +616 -0
  68. runbooks/cfat/src/actions/define-account-type.ts +51 -0
  69. runbooks/cfat/src/actions/get-enabled-org-policy-types.ts +40 -0
  70. runbooks/cfat/src/actions/get-enabled-org-services.ts +26 -0
  71. runbooks/cfat/src/actions/get-idc-info.ts +34 -0
  72. runbooks/cfat/src/actions/get-org-da-accounts.ts +34 -0
  73. runbooks/cfat/src/actions/get-org-details.ts +35 -0
  74. runbooks/cfat/src/actions/get-org-member-accounts.ts +44 -0
  75. runbooks/cfat/src/actions/get-org-ous.ts +35 -0
  76. runbooks/cfat/src/actions/get-regions.ts +22 -0
  77. runbooks/cfat/src/actions/zip-assessment.ts +27 -0
  78. runbooks/cfat/src/types/index.d.ts +147 -0
  79. runbooks/cfat/tests/__init__.py +141 -0
  80. runbooks/cfat/tests/test_cli.py +340 -0
  81. runbooks/cfat/tests/test_integration.py +290 -0
  82. runbooks/cfat/tests/test_models.py +505 -0
  83. runbooks/cfat/tests/test_reporting.py +354 -0
  84. runbooks/cfat/tsconfig.json +16 -0
  85. runbooks/cfat/webpack.config.cjs +27 -0
  86. runbooks/config.py +260 -0
  87. runbooks/finops/__init__.py +88 -0
  88. runbooks/finops/aws_client.py +245 -0
  89. runbooks/finops/cli.py +151 -0
  90. runbooks/finops/cost_processor.py +410 -0
  91. runbooks/finops/dashboard_runner.py +448 -0
  92. runbooks/finops/helpers.py +355 -0
  93. runbooks/finops/main.py +14 -0
  94. runbooks/finops/profile_processor.py +174 -0
  95. runbooks/finops/types.py +66 -0
  96. runbooks/finops/visualisations.py +80 -0
  97. runbooks/inventory/.gitignore +354 -0
  98. runbooks/inventory/ArgumentsClass.py +261 -0
  99. runbooks/inventory/Inventory_Modules.py +6130 -0
  100. runbooks/inventory/LandingZone/delete_lz.py +1075 -0
  101. runbooks/inventory/README.md +1320 -0
  102. runbooks/inventory/__init__.py +62 -0
  103. runbooks/inventory/account_class.py +532 -0
  104. runbooks/inventory/all_my_instances_wrapper.py +123 -0
  105. runbooks/inventory/aws_decorators.py +201 -0
  106. runbooks/inventory/cfn_move_stack_instances.py +1526 -0
  107. runbooks/inventory/check_cloudtrail_compliance.py +614 -0
  108. runbooks/inventory/check_controltower_readiness.py +1107 -0
  109. runbooks/inventory/check_landingzone_readiness.py +711 -0
  110. runbooks/inventory/cloudtrail.md +727 -0
  111. runbooks/inventory/collectors/__init__.py +20 -0
  112. runbooks/inventory/collectors/aws_compute.py +518 -0
  113. runbooks/inventory/collectors/aws_networking.py +275 -0
  114. runbooks/inventory/collectors/base.py +222 -0
  115. runbooks/inventory/core/__init__.py +19 -0
  116. runbooks/inventory/core/collector.py +303 -0
  117. runbooks/inventory/core/formatter.py +296 -0
  118. runbooks/inventory/delete_s3_buckets_objects.py +169 -0
  119. runbooks/inventory/discovery.md +81 -0
  120. runbooks/inventory/draw_org_structure.py +748 -0
  121. runbooks/inventory/ec2_vpc_utils.py +341 -0
  122. runbooks/inventory/find_cfn_drift_detection.py +272 -0
  123. runbooks/inventory/find_cfn_orphaned_stacks.py +719 -0
  124. runbooks/inventory/find_cfn_stackset_drift.py +733 -0
  125. runbooks/inventory/find_ec2_security_groups.py +669 -0
  126. runbooks/inventory/find_landingzone_versions.py +201 -0
  127. runbooks/inventory/find_vpc_flow_logs.py +1221 -0
  128. runbooks/inventory/inventory.sh +659 -0
  129. runbooks/inventory/list_cfn_stacks.py +558 -0
  130. runbooks/inventory/list_cfn_stackset_operation_results.py +252 -0
  131. runbooks/inventory/list_cfn_stackset_operations.py +734 -0
  132. runbooks/inventory/list_cfn_stacksets.py +453 -0
  133. runbooks/inventory/list_config_recorders_delivery_channels.py +681 -0
  134. runbooks/inventory/list_ds_directories.py +354 -0
  135. runbooks/inventory/list_ec2_availability_zones.py +286 -0
  136. runbooks/inventory/list_ec2_ebs_volumes.py +244 -0
  137. runbooks/inventory/list_ec2_instances.py +425 -0
  138. runbooks/inventory/list_ecs_clusters_and_tasks.py +562 -0
  139. runbooks/inventory/list_elbs_load_balancers.py +411 -0
  140. runbooks/inventory/list_enis_network_interfaces.py +526 -0
  141. runbooks/inventory/list_guardduty_detectors.py +568 -0
  142. runbooks/inventory/list_iam_policies.py +404 -0
  143. runbooks/inventory/list_iam_roles.py +518 -0
  144. runbooks/inventory/list_iam_saml_providers.py +359 -0
  145. runbooks/inventory/list_lambda_functions.py +882 -0
  146. runbooks/inventory/list_org_accounts.py +446 -0
  147. runbooks/inventory/list_org_accounts_users.py +354 -0
  148. runbooks/inventory/list_rds_db_instances.py +406 -0
  149. runbooks/inventory/list_route53_hosted_zones.py +318 -0
  150. runbooks/inventory/list_servicecatalog_provisioned_products.py +575 -0
  151. runbooks/inventory/list_sns_topics.py +360 -0
  152. runbooks/inventory/list_ssm_parameters.py +402 -0
  153. runbooks/inventory/list_vpc_subnets.py +433 -0
  154. runbooks/inventory/list_vpcs.py +422 -0
  155. runbooks/inventory/lockdown_cfn_stackset_role.py +224 -0
  156. runbooks/inventory/models/__init__.py +24 -0
  157. runbooks/inventory/models/account.py +192 -0
  158. runbooks/inventory/models/inventory.py +309 -0
  159. runbooks/inventory/models/resource.py +247 -0
  160. runbooks/inventory/recover_cfn_stack_ids.py +205 -0
  161. runbooks/inventory/requirements.txt +12 -0
  162. runbooks/inventory/run_on_multi_accounts.py +211 -0
  163. runbooks/inventory/tests/common_test_data.py +3661 -0
  164. runbooks/inventory/tests/common_test_functions.py +204 -0
  165. runbooks/inventory/tests/script_test_data.py +0 -0
  166. runbooks/inventory/tests/setup.py +24 -0
  167. runbooks/inventory/tests/src.py +18 -0
  168. runbooks/inventory/tests/test_cfn_describe_stacks.py +208 -0
  169. runbooks/inventory/tests/test_ec2_describe_instances.py +162 -0
  170. runbooks/inventory/tests/test_inventory_modules.py +55 -0
  171. runbooks/inventory/tests/test_lambda_list_functions.py +86 -0
  172. runbooks/inventory/tests/test_moto_integration_example.py +273 -0
  173. runbooks/inventory/tests/test_org_list_accounts.py +49 -0
  174. runbooks/inventory/update_aws_actions.py +173 -0
  175. runbooks/inventory/update_cfn_stacksets.py +1215 -0
  176. runbooks/inventory/update_cloudwatch_logs_retention_policy.py +294 -0
  177. runbooks/inventory/update_iam_roles_cross_accounts.py +478 -0
  178. runbooks/inventory/update_s3_public_access_block.py +539 -0
  179. runbooks/inventory/utils/__init__.py +23 -0
  180. runbooks/inventory/utils/aws_helpers.py +510 -0
  181. runbooks/inventory/utils/threading_utils.py +493 -0
  182. runbooks/inventory/utils/validation.py +682 -0
  183. runbooks/inventory/verify_ec2_security_groups.py +1430 -0
  184. runbooks/main.py +785 -0
  185. runbooks/organizations/__init__.py +12 -0
  186. runbooks/organizations/manager.py +374 -0
  187. runbooks/security_baseline/README.md +324 -0
  188. runbooks/security_baseline/checklist/alternate_contacts.py +8 -1
  189. runbooks/security_baseline/checklist/bucket_public_access.py +4 -1
  190. runbooks/security_baseline/checklist/cloudwatch_alarm_configuration.py +9 -2
  191. runbooks/security_baseline/checklist/guardduty_enabled.py +9 -2
  192. runbooks/security_baseline/checklist/multi_region_instance_usage.py +5 -1
  193. runbooks/security_baseline/checklist/root_access_key.py +6 -1
  194. runbooks/security_baseline/config-origin.json +1 -1
  195. runbooks/security_baseline/config.json +1 -1
  196. runbooks/security_baseline/permission.json +1 -1
  197. runbooks/security_baseline/report_generator.py +10 -2
  198. runbooks/security_baseline/report_template_en.html +7 -7
  199. runbooks/security_baseline/report_template_jp.html +7 -7
  200. runbooks/security_baseline/report_template_kr.html +12 -12
  201. runbooks/security_baseline/report_template_vn.html +7 -7
  202. runbooks/security_baseline/requirements.txt +7 -0
  203. runbooks/security_baseline/run_script.py +8 -2
  204. runbooks/security_baseline/security_baseline_tester.py +10 -2
  205. runbooks/security_baseline/utils/common.py +5 -1
  206. runbooks/utils/__init__.py +204 -0
  207. runbooks-0.6.1.dist-info/METADATA +373 -0
  208. runbooks-0.6.1.dist-info/RECORD +237 -0
  209. {runbooks-0.2.5.dist-info → runbooks-0.6.1.dist-info}/WHEEL +1 -1
  210. runbooks-0.6.1.dist-info/entry_points.txt +7 -0
  211. runbooks-0.6.1.dist-info/licenses/LICENSE +201 -0
  212. runbooks-0.6.1.dist-info/top_level.txt +3 -0
  213. runbooks/python101/calculator.py +0 -34
  214. runbooks/python101/config.py +0 -1
  215. runbooks/python101/exceptions.py +0 -16
  216. runbooks/python101/file_manager.py +0 -218
  217. runbooks/python101/toolkit.py +0 -153
  218. runbooks-0.2.5.dist-info/METADATA +0 -439
  219. runbooks-0.2.5.dist-info/RECORD +0 -61
  220. runbooks-0.2.5.dist-info/entry_points.txt +0 -3
  221. 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
- runbooks
2
+ CloudOps Runbooks - Enterprise CloudOps Automation Toolkit
3
3
 
4
- Provides utility functions for math operations, AWS S3 interactions, and data management.
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
- # __version__ = "0.2.5"
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
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ CloudOps Runbooks CLI Entry Point
4
+
5
+ This module enables the package to be executed as:
6
+ python -m runbooks
7
+ uv run python -m runbooks
8
+ """
9
+
10
+ from runbooks.main import main
11
+
12
+ if __name__ == "__main__":
13
+ main()
@@ -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(Resources=instance_ids, Tags=[{"Key": k, "Value": v} for k, v in tags.items()])
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 {"statusCode": 200, "body": json.dumps({"message": "Instances launched", "InstanceIDs": instance_ids})}
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 ## For CLI mode support
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 BotoCoreError, ClientError, NoCredentialsError, PartialCredentialsError
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 {"statusCode": 200, "body": json.dumps(f"Action '{action}' completed successfully.")}
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("--action", choices=["start", "stop"], required=True, help="Action to perform (start/stop).")
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(TopicArn=SNS_TOPIC_ARN, Subject="Unused EBS Volumes Report", Message=email_body)
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:
@@ -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 ## Import reusable 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, ACL="private", CreateBucketConfiguration={"LocationConstraint": region}
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']}.")
@@ -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("--max-keys", type=int, default=1000, help="Max number of keys to fetch (default: 1000).")
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 = ["Account Name", "Functional Area", "WBS Code", "Business Unit", "Managed by", "CostGroup", "TechOwner"]
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 {"statusCode": 200, "body": json.dumps(f"Tags successfully applied to instance {instance_id}")}
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
+ ]