ddapm-test-agent 1.36.0__py3-none-any.whl → 1.38.0__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.
- ddapm_test_agent/agent.py +98 -13
- ddapm_test_agent/remoteconfig.py +2 -2
- ddapm_test_agent/static/style.css +1679 -0
- ddapm_test_agent/templates/base.html +31 -0
- ddapm_test_agent/templates/config.html +440 -0
- ddapm_test_agent/templates/dashboard.html +90 -0
- ddapm_test_agent/templates/macros.html +40 -0
- ddapm_test_agent/templates/requests.html +1925 -0
- ddapm_test_agent/templates/session_detail.html +37 -0
- ddapm_test_agent/templates/sessions.html +23 -0
- ddapm_test_agent/templates/snapshot_detail.html +410 -0
- ddapm_test_agent/templates/snapshots.html +86 -0
- ddapm_test_agent/templates/trace_detail.html +37 -0
- ddapm_test_agent/templates/tracer_flares.html +640 -0
- ddapm_test_agent/templates/traces.html +24 -0
- ddapm_test_agent/vcr_proxy.py +306 -58
- ddapm_test_agent/web.py +1523 -0
- {ddapm_test_agent-1.36.0.dist-info → ddapm_test_agent-1.38.0.dist-info}/METADATA +15 -5
- ddapm_test_agent-1.38.0.dist-info/RECORD +40 -0
- ddapm_test_agent-1.36.0.dist-info/RECORD +0 -26
- {ddapm_test_agent-1.36.0.dist-info → ddapm_test_agent-1.38.0.dist-info}/WHEEL +0 -0
- {ddapm_test_agent-1.36.0.dist-info → ddapm_test_agent-1.38.0.dist-info}/entry_points.txt +0 -0
- {ddapm_test_agent-1.36.0.dist-info → ddapm_test_agent-1.38.0.dist-info}/licenses/LICENSE.BSD3 +0 -0
- {ddapm_test_agent-1.36.0.dist-info → ddapm_test_agent-1.38.0.dist-info}/licenses/LICENSE.apache2 +0 -0
- {ddapm_test_agent-1.36.0.dist-info → ddapm_test_agent-1.38.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>{% block title %}Test Agent - {{ title }}{% endblock %}</title>
|
|
7
|
+
<link rel="stylesheet" href="/static/style.css">
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<nav class="navbar">
|
|
11
|
+
<div class="nav-container">
|
|
12
|
+
<div class="nav-brand">
|
|
13
|
+
<h1>Test Agent</h1>
|
|
14
|
+
</div>
|
|
15
|
+
<ul class="nav-links">
|
|
16
|
+
<li><a href="/" class="nav-link">Dashboard</a></li>
|
|
17
|
+
<li><a href="/requests" class="nav-link">Requests</a></li>
|
|
18
|
+
<li><a href="/config" class="nav-link">Remote Config</a></li>
|
|
19
|
+
<li><a href="/tracerflares" class="nav-link">Tracer Flares</a></li>
|
|
20
|
+
<li><a href="/snapshots" class="nav-link">Snapshots</a></li>
|
|
21
|
+
</ul>
|
|
22
|
+
</div>
|
|
23
|
+
</nav>
|
|
24
|
+
|
|
25
|
+
<main class="main-content">
|
|
26
|
+
{% block content %}
|
|
27
|
+
{% endblock %}
|
|
28
|
+
</main>
|
|
29
|
+
|
|
30
|
+
</body>
|
|
31
|
+
</html>
|
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
{% extends "base.html" %}
|
|
2
|
+
|
|
3
|
+
{% block content %}
|
|
4
|
+
<div class="config-page">
|
|
5
|
+
<div class="page-header">
|
|
6
|
+
<h2>Remote Configuration Management</h2>
|
|
7
|
+
<p>Manually control remote config settings, files, and tasks to simulate remote config behavior.</p>
|
|
8
|
+
</div>
|
|
9
|
+
|
|
10
|
+
<div class="config-container">
|
|
11
|
+
<!-- Session Token Selection -->
|
|
12
|
+
<div class="config-section">
|
|
13
|
+
<h3>Session Management</h3>
|
|
14
|
+
<div class="form-row">
|
|
15
|
+
<label for="session-select">Select Session Token:</label>
|
|
16
|
+
<select id="session-select" class="form-control">
|
|
17
|
+
{% for token in session_tokens %}
|
|
18
|
+
{% if token is none %}
|
|
19
|
+
<option value="" {% if selected_token is none %}selected{% endif %}>Default (No Token)</option>
|
|
20
|
+
{% else %}
|
|
21
|
+
<option value="{{ token }}" {% if selected_token == token %}selected{% endif %}>{{ token }}</option>
|
|
22
|
+
{% endif %}
|
|
23
|
+
{% endfor %}
|
|
24
|
+
</select>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
<!-- Config Management Actions -->
|
|
29
|
+
<div class="config-section">
|
|
30
|
+
<h3>Configuration Actions</h3>
|
|
31
|
+
<div class="config-tabs">
|
|
32
|
+
<button class="tab-button active" onclick="switchTab(event, 'raw-config')">Raw Config</button>
|
|
33
|
+
<button class="tab-button" onclick="switchTab(event, 'path-config')">Path Config</button>
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<!-- Raw Config Tab -->
|
|
37
|
+
<div id="raw-config" class="tab-content active">
|
|
38
|
+
<div class="form-group">
|
|
39
|
+
<label for="raw-config-data">Configuration Data (JSON):</label>
|
|
40
|
+
<textarea id="raw-config-data" class="config-textarea" placeholder="Enter JSON configuration data...">{{ current_config_json }}</textarea>
|
|
41
|
+
</div>
|
|
42
|
+
<div class="form-actions">
|
|
43
|
+
<button onclick="createConfig()" class="btn btn-primary">Create Config</button>
|
|
44
|
+
<button onclick="updateConfig()" class="btn btn-secondary">Update Config</button>
|
|
45
|
+
<button onclick="clearConfig()" class="btn btn-danger">Clear Config</button>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
<!-- Path Config Tab -->
|
|
50
|
+
<div id="path-config" class="tab-content">
|
|
51
|
+
<div class="form-group">
|
|
52
|
+
<label for="config-path">Configuration Path:</label>
|
|
53
|
+
<input type="text" id="config-path" class="form-control" placeholder="e.g., datadog/2/LIVE_DEBUGGING/enable_live_debugging/config">
|
|
54
|
+
</div>
|
|
55
|
+
<div class="form-group">
|
|
56
|
+
<label for="config-message">Configuration Message (JSON):</label>
|
|
57
|
+
<textarea id="config-message" class="config-textarea" placeholder="Enter JSON message data...">{}</textarea>
|
|
58
|
+
</div>
|
|
59
|
+
<div class="form-actions">
|
|
60
|
+
<button onclick="createPathConfig()" class="btn btn-primary">Create Path Config</button>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
<!-- Current Configuration Display -->
|
|
66
|
+
<div class="config-section">
|
|
67
|
+
<h3>Current Configuration</h3>
|
|
68
|
+
<div class="config-display">
|
|
69
|
+
<pre id="current-config-display">{{ config_data }}</pre>
|
|
70
|
+
</div>
|
|
71
|
+
<button onclick="refreshConfig()" class="btn btn-info">Refresh</button>
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
<!-- Status Messages -->
|
|
75
|
+
<div id="status-messages" class="status-messages"></div>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
<style>
|
|
80
|
+
.config-page {
|
|
81
|
+
padding: 20px;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.config-section {
|
|
85
|
+
background: #f8f9fa;
|
|
86
|
+
border: 1px solid #dee2e6;
|
|
87
|
+
border-radius: 8px;
|
|
88
|
+
padding: 20px;
|
|
89
|
+
margin-bottom: 20px;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.config-section h3 {
|
|
93
|
+
margin-top: 0;
|
|
94
|
+
color: #495057;
|
|
95
|
+
border-bottom: 2px solid #007bff;
|
|
96
|
+
padding-bottom: 10px;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.form-row {
|
|
100
|
+
display: flex;
|
|
101
|
+
align-items: center;
|
|
102
|
+
gap: 10px;
|
|
103
|
+
margin-bottom: 10px;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.form-group {
|
|
107
|
+
margin-bottom: 15px;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.form-group label {
|
|
111
|
+
display: block;
|
|
112
|
+
margin-bottom: 5px;
|
|
113
|
+
font-weight: 600;
|
|
114
|
+
color: #495057;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.form-control {
|
|
118
|
+
width: 100%;
|
|
119
|
+
padding: 8px 12px;
|
|
120
|
+
border: 1px solid #ced4da;
|
|
121
|
+
border-radius: 4px;
|
|
122
|
+
font-size: 14px;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.config-textarea {
|
|
126
|
+
width: 100%;
|
|
127
|
+
height: 200px;
|
|
128
|
+
padding: 12px;
|
|
129
|
+
border: 1px solid #ced4da;
|
|
130
|
+
border-radius: 4px;
|
|
131
|
+
font-family: 'Courier New', monospace;
|
|
132
|
+
font-size: 13px;
|
|
133
|
+
resize: vertical;
|
|
134
|
+
background: #ffffff;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.config-tabs {
|
|
138
|
+
margin-bottom: 20px;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.tab-button {
|
|
142
|
+
background: #e9ecef;
|
|
143
|
+
border: 1px solid #dee2e6;
|
|
144
|
+
padding: 10px 20px;
|
|
145
|
+
cursor: pointer;
|
|
146
|
+
border-radius: 4px 4px 0 0;
|
|
147
|
+
margin-right: 5px;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.tab-button.active {
|
|
151
|
+
background: #007bff;
|
|
152
|
+
color: white;
|
|
153
|
+
border-color: #007bff;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.tab-content {
|
|
157
|
+
display: none;
|
|
158
|
+
border: 1px solid #dee2e6;
|
|
159
|
+
padding: 20px;
|
|
160
|
+
border-radius: 0 4px 4px 4px;
|
|
161
|
+
background: white;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.tab-content.active {
|
|
165
|
+
display: block;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.form-actions {
|
|
169
|
+
display: flex;
|
|
170
|
+
gap: 10px;
|
|
171
|
+
margin-top: 20px;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.btn {
|
|
175
|
+
padding: 8px 16px;
|
|
176
|
+
border: none;
|
|
177
|
+
border-radius: 4px;
|
|
178
|
+
cursor: pointer;
|
|
179
|
+
font-size: 14px;
|
|
180
|
+
text-decoration: none;
|
|
181
|
+
display: inline-block;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.btn-primary {
|
|
185
|
+
background: #007bff;
|
|
186
|
+
color: white;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.btn-secondary {
|
|
190
|
+
background: #6c757d;
|
|
191
|
+
color: white;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.btn-danger {
|
|
195
|
+
background: #dc3545;
|
|
196
|
+
color: white;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.btn-info {
|
|
200
|
+
background: #17a2b8;
|
|
201
|
+
color: white;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.btn:hover {
|
|
205
|
+
opacity: 0.9;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.config-display {
|
|
209
|
+
background: #f8f9fa;
|
|
210
|
+
border: 1px solid #dee2e6;
|
|
211
|
+
border-radius: 4px;
|
|
212
|
+
padding: 15px;
|
|
213
|
+
max-height: 400px;
|
|
214
|
+
overflow-y: auto;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.config-display pre {
|
|
218
|
+
margin: 0;
|
|
219
|
+
font-family: 'Courier New', monospace;
|
|
220
|
+
font-size: 12px;
|
|
221
|
+
white-space: pre-wrap;
|
|
222
|
+
word-break: break-all;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.status-messages {
|
|
226
|
+
position: fixed;
|
|
227
|
+
top: 20px;
|
|
228
|
+
right: 20px;
|
|
229
|
+
z-index: 1000;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.status-message {
|
|
233
|
+
padding: 10px 15px;
|
|
234
|
+
margin-bottom: 10px;
|
|
235
|
+
border-radius: 4px;
|
|
236
|
+
max-width: 300px;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.status-success {
|
|
240
|
+
background: #d4edda;
|
|
241
|
+
color: #155724;
|
|
242
|
+
border: 1px solid #c3e6cb;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.status-error {
|
|
246
|
+
background: #f8d7da;
|
|
247
|
+
color: #721c24;
|
|
248
|
+
border: 1px solid #f5c6cb;
|
|
249
|
+
}
|
|
250
|
+
</style>
|
|
251
|
+
|
|
252
|
+
<script>
|
|
253
|
+
function switchTab(event, tabName) {
|
|
254
|
+
// Hide all tab contents
|
|
255
|
+
const tabContents = document.getElementsByClassName('tab-content');
|
|
256
|
+
for (let i = 0; i < tabContents.length; i++) {
|
|
257
|
+
tabContents[i].classList.remove('active');
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Remove active class from all tab buttons
|
|
261
|
+
const tabButtons = document.getElementsByClassName('tab-button');
|
|
262
|
+
for (let i = 0; i < tabButtons.length; i++) {
|
|
263
|
+
tabButtons[i].classList.remove('active');
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Show selected tab and mark button as active
|
|
267
|
+
document.getElementById(tabName).classList.add('active');
|
|
268
|
+
event.currentTarget.classList.add('active');
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function showStatus(message, isError = false) {
|
|
272
|
+
const statusContainer = document.getElementById('status-messages');
|
|
273
|
+
const statusDiv = document.createElement('div');
|
|
274
|
+
statusDiv.className = `status-message ${isError ? 'status-error' : 'status-success'}`;
|
|
275
|
+
statusDiv.textContent = message;
|
|
276
|
+
|
|
277
|
+
statusContainer.appendChild(statusDiv);
|
|
278
|
+
|
|
279
|
+
// Auto-remove after 5 seconds
|
|
280
|
+
setTimeout(() => {
|
|
281
|
+
statusContainer.removeChild(statusDiv);
|
|
282
|
+
}, 5000);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function getCurrentToken() {
|
|
286
|
+
const select = document.getElementById('session-select');
|
|
287
|
+
return select.value || null;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
async function createConfig() {
|
|
291
|
+
const token = getCurrentToken();
|
|
292
|
+
const configData = document.getElementById('raw-config-data').value;
|
|
293
|
+
|
|
294
|
+
try {
|
|
295
|
+
const response = await fetch('/config/create', {
|
|
296
|
+
method: 'POST',
|
|
297
|
+
headers: {
|
|
298
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
299
|
+
},
|
|
300
|
+
body: `token=${encodeURIComponent(token || '')}&config_data=${encodeURIComponent(configData)}`
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
const result = await response.json();
|
|
304
|
+
if (response.ok) {
|
|
305
|
+
showStatus(result.message);
|
|
306
|
+
setTimeout(() => refreshConfig(), 1000);
|
|
307
|
+
} else {
|
|
308
|
+
showStatus(result.error || 'Error creating config', true);
|
|
309
|
+
}
|
|
310
|
+
} catch (error) {
|
|
311
|
+
showStatus('Network error: ' + error.message, true);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
async function updateConfig() {
|
|
316
|
+
const token = getCurrentToken();
|
|
317
|
+
const configData = document.getElementById('raw-config-data').value;
|
|
318
|
+
|
|
319
|
+
try {
|
|
320
|
+
const response = await fetch('/config/update', {
|
|
321
|
+
method: 'POST',
|
|
322
|
+
headers: {
|
|
323
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
324
|
+
},
|
|
325
|
+
body: `token=${encodeURIComponent(token || '')}&config_data=${encodeURIComponent(configData)}`
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
const result = await response.json();
|
|
329
|
+
if (response.ok) {
|
|
330
|
+
showStatus(result.message);
|
|
331
|
+
setTimeout(() => refreshConfig(), 1000);
|
|
332
|
+
} else {
|
|
333
|
+
showStatus(result.error || 'Error updating config', true);
|
|
334
|
+
}
|
|
335
|
+
} catch (error) {
|
|
336
|
+
showStatus('Network error: ' + error.message, true);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
async function createPathConfig() {
|
|
341
|
+
const token = getCurrentToken();
|
|
342
|
+
const path = document.getElementById('config-path').value;
|
|
343
|
+
const message = document.getElementById('config-message').value;
|
|
344
|
+
|
|
345
|
+
if (!path.trim()) {
|
|
346
|
+
showStatus('Configuration path is required', true);
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
try {
|
|
351
|
+
const response = await fetch('/config/create_path', {
|
|
352
|
+
method: 'POST',
|
|
353
|
+
headers: {
|
|
354
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
355
|
+
},
|
|
356
|
+
body: `token=${encodeURIComponent(token || '')}&path=${encodeURIComponent(path)}&message=${encodeURIComponent(message)}`
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
const result = await response.json();
|
|
360
|
+
if (response.ok) {
|
|
361
|
+
showStatus(result.message);
|
|
362
|
+
setTimeout(() => refreshConfig(), 1000);
|
|
363
|
+
} else {
|
|
364
|
+
showStatus(result.error || 'Error creating path config', true);
|
|
365
|
+
}
|
|
366
|
+
} catch (error) {
|
|
367
|
+
showStatus('Network error: ' + error.message, true);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
async function clearConfig() {
|
|
372
|
+
const token = getCurrentToken();
|
|
373
|
+
|
|
374
|
+
if (!confirm('Are you sure you want to clear the configuration for this session?')) {
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
try {
|
|
379
|
+
const response = await fetch('/config/clear', {
|
|
380
|
+
method: 'POST',
|
|
381
|
+
headers: {
|
|
382
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
383
|
+
},
|
|
384
|
+
body: `token=${encodeURIComponent(token || '')}`
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
const result = await response.json();
|
|
388
|
+
if (response.ok) {
|
|
389
|
+
showStatus(result.message);
|
|
390
|
+
document.getElementById('raw-config-data').value = '{}';
|
|
391
|
+
setTimeout(() => refreshConfig(), 1000);
|
|
392
|
+
} else {
|
|
393
|
+
showStatus(result.error || 'Error clearing config', true);
|
|
394
|
+
}
|
|
395
|
+
} catch (error) {
|
|
396
|
+
showStatus('Network error: ' + error.message, true);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function refreshConfig() {
|
|
401
|
+
// Navigate to current page with selected token to refresh data
|
|
402
|
+
const token = getCurrentToken();
|
|
403
|
+
const url = new URL(window.location);
|
|
404
|
+
if (token) {
|
|
405
|
+
url.searchParams.set('token', token);
|
|
406
|
+
} else {
|
|
407
|
+
url.searchParams.delete('token');
|
|
408
|
+
}
|
|
409
|
+
window.location.href = url.toString();
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// Initialize the interface
|
|
413
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
414
|
+
// Set up session token change handler
|
|
415
|
+
document.getElementById('session-select').addEventListener('change', function() {
|
|
416
|
+
const token = this.value;
|
|
417
|
+
const url = new URL(window.location);
|
|
418
|
+
if (token) {
|
|
419
|
+
url.searchParams.set('token', token);
|
|
420
|
+
} else {
|
|
421
|
+
url.searchParams.delete('token');
|
|
422
|
+
}
|
|
423
|
+
window.location.href = url.toString();
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
// Format JSON in textareas
|
|
427
|
+
const textareas = document.querySelectorAll('.config-textarea');
|
|
428
|
+
textareas.forEach(textarea => {
|
|
429
|
+
textarea.addEventListener('blur', function() {
|
|
430
|
+
try {
|
|
431
|
+
const parsed = JSON.parse(this.value);
|
|
432
|
+
this.value = JSON.stringify(parsed, null, 2);
|
|
433
|
+
} catch (e) {
|
|
434
|
+
// Invalid JSON, leave as is
|
|
435
|
+
}
|
|
436
|
+
});
|
|
437
|
+
});
|
|
438
|
+
});
|
|
439
|
+
</script>
|
|
440
|
+
{% endblock %}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
{% extends "base.html" %}
|
|
2
|
+
|
|
3
|
+
{% block content %}
|
|
4
|
+
<div class="dashboard">
|
|
5
|
+
<div class="dashboard-header">
|
|
6
|
+
<h2>Agent Configuration Dashboard</h2>
|
|
7
|
+
</div>
|
|
8
|
+
|
|
9
|
+
<div class="dashboard-grid">
|
|
10
|
+
<div class="dashboard-card">
|
|
11
|
+
<div class="card-header">
|
|
12
|
+
<h3>Server Configuration</h3>
|
|
13
|
+
</div>
|
|
14
|
+
<div class="card-content">
|
|
15
|
+
<div class="stat-item">
|
|
16
|
+
<span class="stat-label">Web UI Port:</span>
|
|
17
|
+
<span class="stat-value">{{ web_ui_port }}</span>
|
|
18
|
+
</div>
|
|
19
|
+
<div class="stat-item">
|
|
20
|
+
<span class="stat-label">APM Server Port:</span>
|
|
21
|
+
<span class="stat-value">{{ apm_port }}</span>
|
|
22
|
+
</div>
|
|
23
|
+
<div class="stat-item">
|
|
24
|
+
<span class="stat-label">OTLP HTTP Port:</span>
|
|
25
|
+
<span class="stat-value">{{ otlp_http_port }}</span>
|
|
26
|
+
</div>
|
|
27
|
+
<div class="stat-item">
|
|
28
|
+
<span class="stat-label">OTLP GRPC Port:</span>
|
|
29
|
+
<span class="stat-value">{{ otlp_grpc_port }}</span>
|
|
30
|
+
</div>
|
|
31
|
+
<div class="stat-item">
|
|
32
|
+
<span class="stat-label">Proxying to Agent:</span>
|
|
33
|
+
<span class="stat-value {{ 'proxy-enabled' if is_proxying else 'proxy-disabled' }}">
|
|
34
|
+
{{ 'Yes' if is_proxying else 'No' }}
|
|
35
|
+
</span>
|
|
36
|
+
</div>
|
|
37
|
+
{% if is_proxying %}
|
|
38
|
+
<div class="stat-item">
|
|
39
|
+
<span class="stat-label">Agent URL:</span>
|
|
40
|
+
<span class="stat-value">{{ agent_url }}</span>
|
|
41
|
+
</div>
|
|
42
|
+
{% endif %}
|
|
43
|
+
<div class="stat-item">
|
|
44
|
+
<span class="stat-label">Snapshot Directory:</span>
|
|
45
|
+
<span class="stat-value">{{ snapshot_dir }}</span>
|
|
46
|
+
</div>
|
|
47
|
+
<div class="stat-item">
|
|
48
|
+
<span class="stat-label">VCR Enabled:</span>
|
|
49
|
+
<span class="stat-value">{{ 'Yes' if vcr_enabled else 'No' }}</span>
|
|
50
|
+
</div>
|
|
51
|
+
<div class="stat-item">
|
|
52
|
+
<span class="stat-label">Error Responses Disabled:</span>
|
|
53
|
+
<span class="stat-value">{{ 'Yes' if error_responses_disabled else 'No' }}</span>
|
|
54
|
+
</div>
|
|
55
|
+
<div class="stat-item">
|
|
56
|
+
<span class="stat-label">Max Requests in Memory:</span>
|
|
57
|
+
<span class="stat-value">{{ max_requests }}</span>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
<div class="dashboard-card">
|
|
65
|
+
<div class="card-header">
|
|
66
|
+
<h3>Enabled Servers</h3>
|
|
67
|
+
</div>
|
|
68
|
+
<div class="card-content">
|
|
69
|
+
<div class="stat-item">
|
|
70
|
+
<span class="stat-label">Web UI:</span>
|
|
71
|
+
<span class="stat-value">{{ 'Enabled' if enabled_servers.web_ui else 'Disabled' }}</span>
|
|
72
|
+
</div>
|
|
73
|
+
<div class="stat-item">
|
|
74
|
+
<span class="stat-label">APM Server:</span>
|
|
75
|
+
<span class="stat-value">{{ 'Enabled' if enabled_servers.apm_server else 'Disabled' }}</span>
|
|
76
|
+
</div>
|
|
77
|
+
<div class="stat-item">
|
|
78
|
+
<span class="stat-label">OTLP HTTP Server:</span>
|
|
79
|
+
<span class="stat-value">{{ 'Enabled' if enabled_servers.otlp_http else 'Disabled' }}</span>
|
|
80
|
+
</div>
|
|
81
|
+
<div class="stat-item">
|
|
82
|
+
<span class="stat-label">OTLP GRPC Server:</span>
|
|
83
|
+
<span class="stat-value">{{ 'Enabled' if enabled_servers.otlp_grpc else 'Disabled' }}</span>
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
{% endblock %}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{# Reusable template components #}
|
|
2
|
+
|
|
3
|
+
{# Page layout with consistent header and structure #}
|
|
4
|
+
{% macro page_layout(page_title, subtitle=None, extra_header=None) -%}
|
|
5
|
+
<div class="{{ page_title.lower() }}-page">
|
|
6
|
+
<div class="page-header">
|
|
7
|
+
<h2>{{ page_title }}</h2>
|
|
8
|
+
{% if subtitle %}
|
|
9
|
+
<div class="page-subtitle">
|
|
10
|
+
<span>{{ subtitle }}</span>
|
|
11
|
+
</div>
|
|
12
|
+
{% endif %}
|
|
13
|
+
{% if extra_header %}
|
|
14
|
+
{{ extra_header | safe }}
|
|
15
|
+
{% endif %}
|
|
16
|
+
</div>
|
|
17
|
+
<div class="{{ page_title.lower() }}-container">
|
|
18
|
+
{{ caller() }}
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
{%- endmacro %}
|
|
22
|
+
|
|
23
|
+
{# Empty state component #}
|
|
24
|
+
{% macro empty_state(message) -%}
|
|
25
|
+
<div class="empty-state">
|
|
26
|
+
<p>{{ message }}</p>
|
|
27
|
+
</div>
|
|
28
|
+
{%- endmacro %}
|
|
29
|
+
|
|
30
|
+
{# Error message component #}
|
|
31
|
+
{% macro error_message(message) -%}
|
|
32
|
+
<div class="error-message">
|
|
33
|
+
<p>{{ message }}</p>
|
|
34
|
+
</div>
|
|
35
|
+
{%- endmacro %}
|
|
36
|
+
|
|
37
|
+
{# Action button component #}
|
|
38
|
+
{% macro action_button(url, text, class_name="action-button") -%}
|
|
39
|
+
<a href="{{ url }}" class="{{ class_name }}">{{ text }}</a>
|
|
40
|
+
{%- endmacro %}
|