Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -2,6 +2,8 @@ import streamlit as st
|
|
2 |
import json
|
3 |
from jsonschema import validate, ValidationError
|
4 |
import re
|
|
|
|
|
5 |
|
6 |
# Mock existing resources (customizable)
|
7 |
existing_resources = {
|
@@ -32,7 +34,8 @@ arm_schema = {
|
|
32 |
"type": {"type": "string"},
|
33 |
"name": {"type": "string"},
|
34 |
"location": {"type": "string"},
|
35 |
-
"properties": {"type": "object"}
|
|
|
36 |
}
|
37 |
}
|
38 |
},
|
@@ -62,6 +65,68 @@ def resolve_template_expressions(template):
|
|
62 |
resolved_str = re.sub(r'\[(\w+\.\w+)\]', resolve_expression, template_str)
|
63 |
return json.loads(resolved_str)
|
64 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
65 |
def simulate_deployment(arm_template):
|
66 |
try:
|
67 |
template = json.loads(arm_template)
|
@@ -69,7 +134,7 @@ def simulate_deployment(arm_template):
|
|
69 |
# Validate template
|
70 |
is_valid, validation_message = validate_template(template)
|
71 |
if not is_valid:
|
72 |
-
return [], [], [], [], validation_message
|
73 |
|
74 |
# Resolve expressions
|
75 |
resolved_template = resolve_template_expressions(template)
|
@@ -107,12 +172,21 @@ def simulate_deployment(arm_template):
|
|
107 |
if resource_name not in [r.get('name') for r in resolved_template.get('resources', [])]:
|
108 |
resources_to_delete.append(resource_name)
|
109 |
|
110 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
111 |
except json.JSONDecodeError:
|
112 |
-
return [], [], [], [], "Invalid JSON format. Please check your ARM template."
|
113 |
|
114 |
# Streamlit UI
|
115 |
-
st.title("
|
116 |
st.subheader("Paste your ARM template below to simulate its deployment.")
|
117 |
|
118 |
# Input box for ARM template
|
@@ -121,7 +195,7 @@ template_input = st.text_area("Paste ARM Template JSON here:", height=300)
|
|
121 |
# Button to simulate the evaluation
|
122 |
if st.button("Simulate Template"):
|
123 |
if template_input:
|
124 |
-
resources_to_create, resources_to_update, resources_to_delete, resource_details, message = simulate_deployment(template_input)
|
125 |
|
126 |
st.subheader("Simulation Results:")
|
127 |
st.write(message)
|
@@ -153,6 +227,22 @@ if st.button("Simulate Template"):
|
|
153 |
st.write("**Properties:**")
|
154 |
st.json(resource['properties'])
|
155 |
st.write("---")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
156 |
else:
|
157 |
st.write("No changes detected in the simulation.")
|
158 |
else:
|
|
|
2 |
import json
|
3 |
from jsonschema import validate, ValidationError
|
4 |
import re
|
5 |
+
import networkx as nx
|
6 |
+
import matplotlib.pyplot as plt
|
7 |
|
8 |
# Mock existing resources (customizable)
|
9 |
existing_resources = {
|
|
|
34 |
"type": {"type": "string"},
|
35 |
"name": {"type": "string"},
|
36 |
"location": {"type": "string"},
|
37 |
+
"properties": {"type": "object"},
|
38 |
+
"dependsOn": {"type": "array", "items": {"type": "string"}}
|
39 |
}
|
40 |
}
|
41 |
},
|
|
|
65 |
resolved_str = re.sub(r'\[(\w+\.\w+)\]', resolve_expression, template_str)
|
66 |
return json.loads(resolved_str)
|
67 |
|
68 |
+
def estimate_cost(resources):
|
69 |
+
# This is a simplified cost estimation. In a real scenario, you'd need to
|
70 |
+
# integrate with Azure's pricing API or use a more comprehensive pricing model.
|
71 |
+
cost_estimates = {
|
72 |
+
"Microsoft.Storage/storageAccounts": 10, # $10 per month
|
73 |
+
"Microsoft.Compute/virtualMachines": 50, # $50 per month
|
74 |
+
"Microsoft.Web/sites": 30, # $30 per month
|
75 |
+
"Microsoft.Sql/servers": 40, # $40 per month
|
76 |
+
# Add more resource types and their estimated costs
|
77 |
+
}
|
78 |
+
|
79 |
+
total_cost = 0
|
80 |
+
for resource in resources:
|
81 |
+
resource_type = resource.get('type')
|
82 |
+
if resource_type in cost_estimates:
|
83 |
+
total_cost += cost_estimates[resource_type]
|
84 |
+
else:
|
85 |
+
total_cost += 5 # Default cost for unknown resource types
|
86 |
+
|
87 |
+
return total_cost
|
88 |
+
|
89 |
+
def create_dependency_graph(resources):
|
90 |
+
G = nx.DiGraph()
|
91 |
+
for resource in resources:
|
92 |
+
resource_name = resource.get('name')
|
93 |
+
G.add_node(resource_name)
|
94 |
+
dependencies = resource.get('dependsOn', [])
|
95 |
+
for dep in dependencies:
|
96 |
+
G.add_edge(dep, resource_name)
|
97 |
+
return G
|
98 |
+
|
99 |
+
def plot_dependency_graph(G):
|
100 |
+
plt.figure(figsize=(12, 8))
|
101 |
+
pos = nx.spring_layout(G)
|
102 |
+
nx.draw(G, pos, with_labels=True, node_color='lightblue',
|
103 |
+
node_size=3000, font_size=8, font_weight='bold')
|
104 |
+
edge_labels = nx.get_edge_attributes(G, 'weight')
|
105 |
+
nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels)
|
106 |
+
plt.title("Resource Dependency Graph")
|
107 |
+
return plt
|
108 |
+
|
109 |
+
def lint_template(template):
|
110 |
+
lint_results = []
|
111 |
+
|
112 |
+
# Check 1: Ensure all resources have tags
|
113 |
+
for resource in template.get('resources', []):
|
114 |
+
if 'tags' not in resource:
|
115 |
+
lint_results.append(f"Warning: Resource '{resource.get('name')}' does not have any tags.")
|
116 |
+
|
117 |
+
# Check 2: Ensure parameters have descriptions
|
118 |
+
for param_name, param_value in template.get('parameters', {}).items():
|
119 |
+
if 'metadata' not in param_value or 'description' not in param_value['metadata']:
|
120 |
+
lint_results.append(f"Warning: Parameter '{param_name}' does not have a description.")
|
121 |
+
|
122 |
+
# Check 3: Warn about hardcoded values in resource properties
|
123 |
+
for resource in template.get('resources', []):
|
124 |
+
for prop, value in resource.get('properties', {}).items():
|
125 |
+
if isinstance(value, (str, int, float)) and not isinstance(value, bool):
|
126 |
+
lint_results.append(f"Info: Consider using a parameter for the hardcoded value in resource '{resource.get('name')}', property '{prop}'.")
|
127 |
+
|
128 |
+
return lint_results
|
129 |
+
|
130 |
def simulate_deployment(arm_template):
|
131 |
try:
|
132 |
template = json.loads(arm_template)
|
|
|
134 |
# Validate template
|
135 |
is_valid, validation_message = validate_template(template)
|
136 |
if not is_valid:
|
137 |
+
return [], [], [], [], 0, [], validation_message
|
138 |
|
139 |
# Resolve expressions
|
140 |
resolved_template = resolve_template_expressions(template)
|
|
|
172 |
if resource_name not in [r.get('name') for r in resolved_template.get('resources', [])]:
|
173 |
resources_to_delete.append(resource_name)
|
174 |
|
175 |
+
# Estimate cost
|
176 |
+
estimated_cost = estimate_cost(resource_details)
|
177 |
+
|
178 |
+
# Create dependency graph
|
179 |
+
dependency_graph = create_dependency_graph(resolved_template.get('resources', []))
|
180 |
+
|
181 |
+
# Lint template
|
182 |
+
lint_results = lint_template(template)
|
183 |
+
|
184 |
+
return resources_to_create, resources_to_update, resources_to_delete, resource_details, estimated_cost, dependency_graph, lint_results, "Simulation completed successfully."
|
185 |
except json.JSONDecodeError:
|
186 |
+
return [], [], [], [], 0, None, [], "Invalid JSON format. Please check your ARM template."
|
187 |
|
188 |
# Streamlit UI
|
189 |
+
st.title("Comprehensive ARM Template Simulator")
|
190 |
st.subheader("Paste your ARM template below to simulate its deployment.")
|
191 |
|
192 |
# Input box for ARM template
|
|
|
195 |
# Button to simulate the evaluation
|
196 |
if st.button("Simulate Template"):
|
197 |
if template_input:
|
198 |
+
resources_to_create, resources_to_update, resources_to_delete, resource_details, estimated_cost, dependency_graph, lint_results, message = simulate_deployment(template_input)
|
199 |
|
200 |
st.subheader("Simulation Results:")
|
201 |
st.write(message)
|
|
|
227 |
st.write("**Properties:**")
|
228 |
st.json(resource['properties'])
|
229 |
st.write("---")
|
230 |
+
|
231 |
+
st.write(f"### Estimated Monthly Cost: ${estimated_cost}")
|
232 |
+
|
233 |
+
st.write("### Resource Dependency Graph:")
|
234 |
+
if dependency_graph:
|
235 |
+
fig = plot_dependency_graph(dependency_graph)
|
236 |
+
st.pyplot(fig)
|
237 |
+
else:
|
238 |
+
st.write("No dependencies found between resources.")
|
239 |
+
|
240 |
+
st.write("### Template Lint Results:")
|
241 |
+
if lint_results:
|
242 |
+
for result in lint_results:
|
243 |
+
st.write(result)
|
244 |
+
else:
|
245 |
+
st.write("No linting issues found.")
|
246 |
else:
|
247 |
st.write("No changes detected in the simulation.")
|
248 |
else:
|