Skip to content

Commit 07165af

Browse files
authored
Add files via upload
1 parent 5ffa108 commit 07165af

4 files changed

Lines changed: 342 additions & 0 deletions

File tree

Dockerfile

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Dockerfile
2+
FROM python:3.9-slim
3+
4+
WORKDIR /app
5+
6+
# Update pip and setuptools
7+
RUN pip install --no-cache-dir --upgrade pip setuptools
8+
9+
COPY requirements.txt .
10+
RUN pip install --no-cache-dir -r requirements.txt
11+
12+
COPY app.py .
13+
COPY templates templates/
14+
15+
CMD ["python", "app.py"]

app.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# app.py
2+
from flask import Flask, send_from_directory, jsonify, request
3+
import yaml
4+
import os
5+
from dotenv import load_dotenv, set_key
6+
import tempfile
7+
import shutil
8+
9+
app = Flask(__name__)
10+
11+
CONFIG_DIR = '/app/config'
12+
ENV_FILE = '/app/.env'
13+
ENV_SECRETS_FILE = '/app/env.device-secrets'
14+
15+
def load_yaml(filename):
16+
try:
17+
with open(os.path.join(CONFIG_DIR, filename), 'r') as file:
18+
return yaml.safe_load(file)
19+
except FileNotFoundError:
20+
return {}
21+
22+
def save_yaml(filename, data):
23+
with open(os.path.join(CONFIG_DIR, filename), 'w') as file:
24+
yaml.dump(data, file, default_flow_style=False)
25+
26+
def load_env(filename):
27+
if not os.path.exists(filename):
28+
return {}
29+
load_dotenv(filename)
30+
return {key: os.getenv(key) for key in os.environ}
31+
32+
def save_env(filename, data):
33+
# Create a temporary file
34+
fd, path = tempfile.mkstemp()
35+
try:
36+
with os.fdopen(fd, 'w') as temp:
37+
for key, value in data.items():
38+
temp.write(f"{key}={value}\n")
39+
40+
# Replace the original file with the temporary file
41+
shutil.move(path, filename)
42+
finally:
43+
# Clean up the temporary file in case of an error
44+
if os.path.exists(path):
45+
os.unlink(path)
46+
47+
@app.route('/')
48+
def index():
49+
return send_from_directory('templates', 'index.html')
50+
51+
@app.route('/api/config/<config_type>', methods=['GET', 'POST'])
52+
def handle_config(config_type):
53+
if request.method == 'GET':
54+
if config_type in ['ast_defaults', 'bigip_receivers']:
55+
return jsonify(load_yaml(f'{config_type}.yaml'))
56+
elif config_type == 'env':
57+
return jsonify(load_env(ENV_FILE))
58+
elif request.method == 'POST':
59+
data = request.json
60+
try:
61+
if config_type in ['ast_defaults', 'bigip_receivers']:
62+
save_yaml(f'{config_type}.yaml', data)
63+
elif config_type == 'env':
64+
save_env(ENV_FILE, data)
65+
return jsonify({"status": "success"})
66+
except Exception as e:
67+
app.logger.error(f"Error saving configuration: {str(e)}")
68+
return jsonify({"status": "error", "message": str(e)}), 500
69+
70+
if __name__ == '__main__':
71+
app.run(host='0.0.0.0', port=5000)

requirements.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
flask==2.0.1
2+
werkzeug==2.0.1
3+
pyyaml==5.4.1
4+
python-dotenv==0.19.0

templates/index.html

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
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>AST Configuration</title>
7+
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
8+
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
9+
<style>
10+
body { font-family: Arial, sans-serif; line-height: 1.6; padding: 20px; }
11+
h1, h2, h3 { color: #333; }
12+
.form-group { margin-bottom: 15px; }
13+
label { display: block; margin-bottom: 5px; }
14+
input[type="text"], input[type="number"], input[type="password"], select, textarea {
15+
width: 100%;
16+
padding: 8px;
17+
margin-bottom: 10px;
18+
}
19+
button { background-color: #4CAF50; color: white; padding: 10px 15px; border: none; cursor: pointer; }
20+
button:hover { background-color: #45a049; }
21+
.config-section { background-color: #f9f9f9; padding: 15px; margin-bottom: 20px; border-radius: 5px; }
22+
.receiver { border: 1px solid #ddd; padding: 10px; margin-bottom: 10px; border-radius: 5px; }
23+
.add-receiver { background-color: #008CBA; }
24+
.remove-receiver { background-color: #f44336; }
25+
.info-text { color: #666; font-style: italic; }
26+
</style>
27+
</head>
28+
<body>
29+
<div id="app">
30+
<h1>AST Configuration</h1>
31+
32+
<div class="config-section">
33+
<h2>AST Defaults</h2>
34+
<div class="form-group">
35+
<label for="collection_interval">Collection Interval (seconds)</label>
36+
<input type="number" id="collection_interval" v-model.number="astDefaults.collection_interval">
37+
</div>
38+
<div class="form-group">
39+
<label for="username">Username</label>
40+
<input type="text" id="username" v-model="astDefaults.username">
41+
</div>
42+
<div class="form-group">
43+
<label for="password">Password</label>
44+
<p class="info-text">The password is stored in the .env.device-secrets file for security reasons.</p>
45+
</div>
46+
<div class="form-group">
47+
<label>Data Types</label>
48+
<div v-for="(dataType, key) in astDefaults.data_types" :key="key">
49+
<label>
50+
<input type="checkbox" v-model="dataType.enabled">
51+
{{ key }}
52+
</label>
53+
</div>
54+
</div>
55+
<div class="form-group">
56+
<label>TLS</label>
57+
<div>
58+
<input type="checkbox" id="tls_insecure_skip_verify" v-model="astDefaults.tls.insecure_skip_verify">
59+
<label for="tls_insecure_skip_verify">Insecure Skip Verify</label>
60+
</div>
61+
<div>
62+
<label for="tls_ca_file">CA File Location</label>
63+
<input type="text" id="tls_ca_file" v-model="astDefaults.tls.ca_file">
64+
</div>
65+
</div>
66+
<div class="form-group">
67+
<label for="f5_data_export">F5 Data Export</label>
68+
<input type="checkbox" id="f5_data_export" v-model="astDefaults.f5_data_export">
69+
</div>
70+
<div class="form-group">
71+
<label for="ssl_cert_validity">SSL Certificate Validity (days)</label>
72+
<input type="number" id="ssl_cert_validity" v-model.number="astDefaults.ssl_cert_validity">
73+
</div>
74+
<button @click="saveConfig('ast_defaults')">Save AST Defaults</button>
75+
</div>
76+
77+
<div class="config-section">
78+
<h2>F5 DataFabric Export Parameters</h2>
79+
<p class="info-text">Optional Parameters Required for metrics export to F5 DataFabric</p>
80+
<div class="form-group">
81+
<label for="sensor_secret_token">SENSOR_SECRET_TOKEN</label>
82+
<input type="text" id="sensor_secret_token" v-model="configs.env.SENSOR_SECRET_TOKEN">
83+
</div>
84+
<div class="form-group">
85+
<label for="sensor_id">SENSOR_ID</label>
86+
<input type="text" id="sensor_id" v-model="configs.env.SENSOR_ID">
87+
</div>
88+
<button @click="saveConfig('env')">Save DataFabric Parameters</button>
89+
</div>
90+
91+
<div class="config-section">
92+
<h2>Grafana Environment Variables</h2>
93+
<p class="info-text">These should be updated to more secure values outside of testing environments.</p>
94+
<div class="form-group">
95+
<label for="gf_security_admin_user">GF_SECURITY_ADMIN_USER</label>
96+
<input type="text" id="gf_security_admin_user" v-model="configs.env.GF_SECURITY_ADMIN_USER">
97+
</div>
98+
<div class="form-group">
99+
<label for="gf_security_admin_password">GF_SECURITY_ADMIN_PASSWORD</label>
100+
<input type="password" id="gf_security_admin_password" v-model="configs.env.GF_SECURITY_ADMIN_PASSWORD">
101+
</div>
102+
<button @click="saveConfig('env')">Save Grafana Environment Variables</button>
103+
</div>
104+
105+
<div class="config-section">
106+
<h2>BIG-IP Receivers</h2>
107+
<div v-for="(receiver, index) in bigipReceivers" :key="index" class="receiver">
108+
<h3>Receiver {{ index + 1 }}</h3>
109+
<div class="form-group">
110+
<label>Name</label>
111+
<input type="text" v-model="receiver.name">
112+
</div>
113+
<div class="form-group">
114+
<label>Host</label>
115+
<input type="text" v-model="receiver.host">
116+
</div>
117+
<div class="form-group">
118+
<label>Port</label>
119+
<input type="number" v-model.number="receiver.port">
120+
</div>
121+
<div class="form-group">
122+
<label>Username</label>
123+
<input type="text" v-model="receiver.username">
124+
</div>
125+
<div class="form-group">
126+
<label>Password</label>
127+
<input type="password" v-model="receiver.password">
128+
</div>
129+
<div class="form-group">
130+
<label>Data Groups</label>
131+
<div v-for="(value, key) in receiver.data_groups" :key="key">
132+
<input type="checkbox" :id="'dg-'+index+'-'+key" v-model="receiver.data_groups[key]">
133+
<label :for="'dg-'+index+'-'+key">{{ key }}</label>
134+
</div>
135+
</div>
136+
<button class="remove-receiver" @click="removeReceiver(index)">Remove Receiver</button>
137+
</div>
138+
<button class="add-receiver" @click="addReceiver">Add Receiver</button>
139+
<button @click="saveConfig('bigip_receivers')">Save BIG-IP Receivers</button>
140+
</div>
141+
142+
<div class="config-section">
143+
<h2>Environment Variables</h2>
144+
<div v-for="(value, key) in configs.env" :key="key" class="form-group"
145+
v-if="!['SENSOR_SECRET_TOKEN', 'SENSOR_ID', 'GF_SECURITY_ADMIN_USER', 'GF_SECURITY_ADMIN_PASSWORD',
146+
'GPG_KEY', 'HOME', 'HOSTNAME', 'LANG', 'PATH', 'PYTHON_VERSION'].includes(key)">
147+
<label :for="key">{{ key }}</label>
148+
<input :id="key" v-model="configs.env[key]" type="text">
149+
</div>
150+
<button @click="saveConfig('env')">Save Environment Variables</button>
151+
</div>
152+
</div>
153+
154+
<script>
155+
new Vue({
156+
el: '#app',
157+
data: {
158+
configs: {
159+
env: {
160+
SENSOR_SECRET_TOKEN: '',
161+
SENSOR_ID: '',
162+
GF_SECURITY_ADMIN_USER: 'admin',
163+
GF_SECURITY_ADMIN_PASSWORD: 'admin'
164+
}
165+
},
166+
astDefaults: {
167+
collection_interval: 60,
168+
username: '',
169+
data_types: {
170+
'f5.dns': { enabled: false },
171+
'f5.gtm': { enabled: false },
172+
// Add other data types here as needed
173+
},
174+
tls: {
175+
insecure_skip_verify: false,
176+
ca_file: ''
177+
},
178+
f5_data_export: false,
179+
ssl_cert_validity: 30
180+
},
181+
bigipReceivers: []
182+
},
183+
mounted() {
184+
this.loadAllConfigs();
185+
},
186+
methods: {
187+
loadAllConfigs() {
188+
this.loadConfig('ast_defaults');
189+
this.loadConfig('bigip_receivers');
190+
this.loadConfig('env');
191+
},
192+
loadConfig(type) {
193+
axios.get(`/api/config/${type}`)
194+
.then(response => {
195+
if (type === 'ast_defaults') {
196+
this.astDefaults = {...this.astDefaults, ...response.data};
197+
// Ensure data_types structure is correct
198+
if (!this.astDefaults.data_types['f5.dns']) {
199+
this.$set(this.astDefaults.data_types, 'f5.dns', { enabled: false });
200+
}
201+
if (!this.astDefaults.data_types['f5.gtm']) {
202+
this.$set(this.astDefaults.data_types, 'f5.gtm', { enabled: false });
203+
}
204+
} else if (type === 'bigip_receivers') {
205+
this.bigipReceivers = response.data;
206+
} else if (type === 'env') {
207+
this.configs.env = {...this.configs.env, ...response.data};
208+
}
209+
});
210+
},
211+
saveConfig(type) {
212+
let data;
213+
if (type === 'ast_defaults') {
214+
data = this.astDefaults;
215+
} else if (type === 'bigip_receivers') {
216+
data = this.bigipReceivers;
217+
} else if (type === 'env') {
218+
data = this.configs.env;
219+
}
220+
axios.post(`/api/config/${type}`, data)
221+
.then(() => alert('Configuration saved successfully!'))
222+
.catch(() => alert('Error saving configuration'));
223+
},
224+
addReceiver() {
225+
this.bigipReceivers.push({
226+
name: '',
227+
host: '',
228+
port: 443,
229+
username: '',
230+
password: '',
231+
data_groups: {
232+
virtual_servers: true,
233+
pools: true,
234+
pool_members: true,
235+
ssl_certs: true,
236+
http_monitors: true,
237+
tcp_monitors: true,
238+
apm_monitors: true,
239+
sdn_services: true,
240+
iapp_logs: true,
241+
performance_stats: true
242+
}
243+
});
244+
},
245+
removeReceiver(index) {
246+
this.bigipReceivers.splice(index, 1);
247+
}
248+
}
249+
});
250+
</script>
251+
</body>
252+
</html>

0 commit comments

Comments
 (0)