Admin Megaphone: Email All Users With Active Capsuls 📢
This commit is contained in:
parent
e32ecd5ce2
commit
b3f62018ea
@ -3,7 +3,8 @@ import sys
|
||||
import json
|
||||
import ipaddress
|
||||
from datetime import datetime, timedelta
|
||||
from flask import Blueprint, current_app, render_template, make_response
|
||||
from flask import Blueprint, current_app, render_template, make_response, session, request, redirect, url_for
|
||||
from flask_mail import Message
|
||||
from werkzeug.exceptions import abort
|
||||
from nanoid import generate
|
||||
|
||||
@ -18,11 +19,38 @@ bp = Blueprint("admin", __name__, url_prefix="/admin")
|
||||
@admin_account_required
|
||||
def index():
|
||||
|
||||
# first create the hosts list w/ ip allocation visualization
|
||||
if request.method == "POST":
|
||||
if "csrf-token" not in request.form or request.form['csrf-token'] != session['csrf-token']:
|
||||
return abort(418, f"u want tea")
|
||||
|
||||
if 'action' not in request.form:
|
||||
return abort(400, "action is required")
|
||||
|
||||
if request.form['action'] == "megaphone":
|
||||
emails_list = get_model().all_accounts_with_active_vms()
|
||||
current_app.logger.info(f"sending '{request.form['subject']}' email to {len(emails_list)} users...")
|
||||
for email in emails_list:
|
||||
current_app.logger.info(email)
|
||||
|
||||
current_app.config["FLASK_MAIL_INSTANCE"].send(
|
||||
Message(
|
||||
request.form['subject'],
|
||||
sender=current_app.config["MAIL_DEFAULT_SENDER"],
|
||||
body=request.form['body'],
|
||||
bcc=["forest.n.johnson@gmail.com", "forest@sequentialread.com"]
|
||||
)
|
||||
)
|
||||
current_app.logger.info(f"sending email is done.")
|
||||
return redirect(f"{url_for('admin.index')}")
|
||||
else:
|
||||
return abort(400, "unknown form action")
|
||||
|
||||
|
||||
# first create the hosts list w/ ip allocation visualization from the database
|
||||
#
|
||||
|
||||
hosts = get_model().list_hosts_with_networks(None)
|
||||
vms_by_host_and_network = get_model().non_deleted_vms_by_host_and_network(None)
|
||||
db_hosts = get_model().list_hosts_with_networks(None)
|
||||
db_vms_by_host_and_network = get_model().non_deleted_vms_by_host_and_network(None)
|
||||
network_display_width_px = float(270)
|
||||
#operations = get_model().list_all_operations()
|
||||
|
||||
@ -33,9 +61,9 @@ def index():
|
||||
{'}'}
|
||||
"""]
|
||||
|
||||
vm_by_id = dict()
|
||||
db_vm_by_id = dict()
|
||||
|
||||
for kv in hosts.items():
|
||||
for kv in db_hosts.items():
|
||||
host_id = kv[0]
|
||||
value = kv[1]
|
||||
display_host = dict(name=host_id, networks=value['networks'])
|
||||
@ -48,13 +76,13 @@ def index():
|
||||
network['allocations'] = []
|
||||
network_addresses_width = float((network_end_int-network_start_int)+1)
|
||||
|
||||
if host_id in vms_by_host_and_network:
|
||||
if network['network_name'] in vms_by_host_and_network[host_id]:
|
||||
for vm in vms_by_host_and_network[host_id][network['network_name']]:
|
||||
if host_id in db_vms_by_host_and_network:
|
||||
if network['network_name'] in db_vms_by_host_and_network[host_id]:
|
||||
for vm in db_vms_by_host_and_network[host_id][network['network_name']]:
|
||||
vm['network_name'] = network['network_name']
|
||||
vm['virtual_bridge_name'] = network['virtual_bridge_name']
|
||||
vm['host'] = host_id
|
||||
vm_by_id[vm['id']] = vm
|
||||
db_vm_by_id[vm['id']] = vm
|
||||
ip_address_int = int(ipaddress.ip_address(vm['public_ipv4']))
|
||||
if network_start_int <= ip_address_int and ip_address_int <= network_end_int:
|
||||
allocation = f"{host_id}_{network['network_name']}_{len(network['allocations'])}"
|
||||
@ -76,38 +104,43 @@ def index():
|
||||
# Now creating the capsuls running status ui
|
||||
#
|
||||
|
||||
db_vms = get_model().all_vm_ids_with_desired_state()
|
||||
# TODO will be replaced
|
||||
#virt_vms = current_app.config["HUB_MODEL"].virsh_list()
|
||||
virt_vms_by_host_and_network = current_app.config["HUB_MODEL"].get_all_by_host_and_network()
|
||||
|
||||
virt_vms_dict = dict()
|
||||
for vm in virt_vms:
|
||||
virt_vms_dict[vm["id"]] = vm["state"]
|
||||
# virt_vms_dict = dict()
|
||||
# for vm in virt_vms:
|
||||
# virt_vms_dict[vm["id"]] = vm["state"]
|
||||
|
||||
in_db_but_not_in_virt = []
|
||||
needs_to_be_started = []
|
||||
needs_to_be_started_missing_ipv4 = []
|
||||
# in_db_but_not_in_virt = []
|
||||
# needs_to_be_started = []
|
||||
# needs_to_be_started_missing_ipv4 = []
|
||||
|
||||
# for vm in db_vms:
|
||||
# if vm["id"] not in virt_vms_dict:
|
||||
# in_db_but_not_in_virt.append(vm["id"])
|
||||
# elif vm["desired_state"] == "running" and virt_vms_dict[vm["id"]] != "running":
|
||||
# if vm["id"] in db_vm_by_id:
|
||||
# needs_to_be_started.append(db_vm_by_id[vm["id"]])
|
||||
# else:
|
||||
# needs_to_be_started_missing_ipv4.append(vm["id"])
|
||||
# elif vm["ipv4"] != current_ipv4
|
||||
|
||||
# current_app.logger.info(f"list_of_networks: {json.dumps(list_of_networks)}")
|
||||
|
||||
for vm in db_vms:
|
||||
if vm["id"] not in virt_vms_dict:
|
||||
in_db_but_not_in_virt.append(vm["id"])
|
||||
elif vm["desired_state"] == "running" and virt_vms_dict[vm["id"]] != "running":
|
||||
if vm["id"] in vm_by_id:
|
||||
needs_to_be_started.append(vm_by_id[vm["id"]])
|
||||
else:
|
||||
needs_to_be_started_missing_ipv4.append(vm["id"])
|
||||
elif vm["ipv4"] != current_ipv4
|
||||
|
||||
csp_inline_style_nonce = generate(alphabet="1234567890qwertyuiopasdfghjklzxcvbnm", size=10)
|
||||
response_text = render_template(
|
||||
"admin.html",
|
||||
csrf_token=session["csrf-token"],
|
||||
display_hosts=display_hosts,
|
||||
in_db_but_not_in_virt=in_db_but_not_in_virt,
|
||||
needs_to_be_started=needs_to_be_started,
|
||||
needs_to_be_started_missing_ipv4=needs_to_be_started_missing_ipv4,
|
||||
# in_db_but_not_in_virt=in_db_but_not_in_virt,
|
||||
# needs_to_be_started=needs_to_be_started,
|
||||
# needs_to_be_started_missing_ipv4=needs_to_be_started_missing_ipv4,
|
||||
network_display_width_px=network_display_width_px,
|
||||
csp_inline_style_nonce=csp_inline_style_nonce,
|
||||
inline_style='\n'.join(inline_styles)
|
||||
inline_style='\n'.join(inline_styles),
|
||||
|
||||
db_vms_by_host_and_network=json.dumps(db_vms_by_host_and_network),
|
||||
virt_vms_by_host_and_network=json.dumps(virt_vms_by_host_and_network),
|
||||
)
|
||||
|
||||
response = make_response(response_text)
|
||||
|
@ -86,9 +86,9 @@ class DBModel:
|
||||
|
||||
return hosts
|
||||
|
||||
# def all_vm_ids_with_desired_state(self):
|
||||
# self.cursor.execute("SELECT id, desired_state FROM vms WHERE deleted IS NULL")
|
||||
# return list(map(lambda x: {"id": x[0], "desired_state": x[1]}, self.cursor.fetchall()))
|
||||
def all_accounts_with_active_vms(self):
|
||||
self.cursor.execute("SELECT DISTINCT email FROM vms WHERE deleted IS NULL")
|
||||
return list(map(lambda x: x[0], self.cursor.fetchall()))
|
||||
|
||||
def operating_systems_dict(self):
|
||||
self.cursor.execute("SELECT id, template_image_file_name, description FROM os_images WHERE deprecated = FALSE")
|
||||
|
@ -39,7 +39,9 @@ class MockSpoke(VirtualizationInterface):
|
||||
return VirtualMachine(id, current_app.config["SPOKE_HOST_ID"], ipv4=ipv4, state="running")
|
||||
|
||||
def get_all_by_host_and_network(self) -> dict:
|
||||
return get_model().non_deleted_vms_by_host_and_network(None)
|
||||
to_return = get_model().non_deleted_vms_by_host_and_network(None)
|
||||
current_app.logger.info(f"MOCK get_all_by_host_and_network: {json.dumps(to_return)}")
|
||||
return to_return
|
||||
|
||||
def create(self, email: str, id: str, template_image_file_name: str, vcpus: int, memory_mb: int, ssh_authorized_keys: list, network_name: str, public_ipv4: str):
|
||||
validate_capsul_id(id)
|
||||
@ -139,6 +141,8 @@ class ShellScriptSpoke(VirtualizationInterface):
|
||||
self.validate_completed_process(vm_list_process)
|
||||
list_of_vms = json.loads(vm_list_process.stdout.decode("utf-8"))
|
||||
|
||||
current_app.logger.info(f"list_of_vms: {json.dumps(list_of_vms)}")
|
||||
|
||||
vm_state_by_id = dict()
|
||||
for vm in list_of_vms:
|
||||
vm_state_by_id[vm['id']] = vm['state']
|
||||
@ -147,6 +151,8 @@ class ShellScriptSpoke(VirtualizationInterface):
|
||||
self.validate_completed_process(net_list_process)
|
||||
list_of_networks = json.loads(net_list_process.stdout.decode("utf-8"))
|
||||
|
||||
current_app.logger.info(f"list_of_networks: {json.dumps(list_of_networks)}")
|
||||
|
||||
networks = dict()
|
||||
vms_by_id = dict()
|
||||
vm_id_by_mac = dict()
|
||||
|
@ -378,3 +378,17 @@ footer {
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
form.megaphone {
|
||||
margin-top: 1rem;
|
||||
width: 640px;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
form.megaphone input[type=text],
|
||||
form.megaphone textarea {
|
||||
width: 100%;
|
||||
}
|
||||
form.megaphone textarea {
|
||||
height: 360px;
|
||||
}
|
@ -36,6 +36,43 @@
|
||||
|
||||
<hr/>
|
||||
{% endfor %}
|
||||
|
||||
<div class="row">
|
||||
<h1>db_vms_by_host_and_network</h1>
|
||||
</div>
|
||||
<div class="row">
|
||||
<pre>
|
||||
{{db_vms_by_host_and_network}}
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<h1>virt_vms_by_host_and_network</h1>
|
||||
</div>
|
||||
<div class="row">
|
||||
<pre>
|
||||
{{virt_vms_by_host_and_network}}
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div class="third-margin">
|
||||
<div class="row">
|
||||
<h1>📢 Admin Megaphone: Email All Users With Active Capsuls 📢</h1>
|
||||
</div>
|
||||
<div class="row">
|
||||
<form method="post" class="megaphone">
|
||||
<input type="hidden" name="action" value="megaphone"></input>
|
||||
<input type="hidden" name="csrf-token" value="{{ csrf_token }}"/>
|
||||
<input type="text" name="subject" placeholder="Capsul Maintenance blahblahblah" />
|
||||
<textarea name="body" placeholder="Hello, ..."></textarea>
|
||||
<input type="submit" value="SEND"/>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user