···1111from capsulflask.metrics import durations as metric_durations
1212from capsulflask.auth import admin_account_required
1313from capsulflask.db import get_model
1414+from capsulflask.consistency import get_all_vms_from_db, get_all_vms_from_hosts
1415from capsulflask.shared import my_exec_info_message
15161617bp = Blueprint("admin", __name__, url_prefix="/admin")
···4849 else:
4950 return abort(400, "unknown form action")
50515252+ # moving on from the form post stuff...
51535254 # first create the hosts list w/ ip allocation visualization from the database
5355 #
54565557 db_hosts = get_model().list_hosts_with_networks(None)
5656- db_vms_by_host_and_network = get_model().non_deleted_vms_by_host_and_network(None)
5858+ db_vms_by_id = get_all_vms_from_db()
5759 network_display_width_px = float(270)
5860 #operations = get_model().list_all_operations()
5961···6466 {'}'}
6567 """]
66686767- db_vm_by_id = dict()
6969+ db_vms_by_host_network = dict()
7070+ for vm in db_vms_by_id.values():
7171+ host_network_key = f"{vm['host']}_{vm['network_name']}"
7272+ if host_network_key not in db_vms_by_host_network:
7373+ db_vms_by_host_network[host_network_key] = []
7474+ db_vms_by_host_network[host_network_key].append(vm)
7575+68766977 for kv in db_hosts.items():
7078 host_id = kv[0]
···7987 network['allocations'] = []
8088 network_addresses_width = float((network_end_int-network_start_int)+1)
81898282- if host_id in db_vms_by_host_and_network:
8383- if network['network_name'] in db_vms_by_host_and_network[host_id]:
8484- for vm in db_vms_by_host_and_network[host_id][network['network_name']]:
8585- vm['network_name'] = network['network_name']
8686- vm['virtual_bridge_name'] = network['virtual_bridge_name']
8787- vm['host'] = host_id
8888- db_vm_by_id[vm['id']] = vm
8989- ip_address_int = int(ipaddress.ip_address(vm['public_ipv4']))
9090- if network_start_int <= ip_address_int and ip_address_int <= network_end_int:
9191- allocation = f"{host_id}_{network['network_name']}_{len(network['allocations'])}"
9292- inline_styles.append(
9393- f"""
9494- .{allocation} {'{'}
9595- left: {(float(ip_address_int-network_start_int)/network_addresses_width)*network_display_width_px}px;
9696- width: {network_display_width_px/network_addresses_width}px;
9797- {'}'}
9898- """
9999- )
100100- network['allocations'].append(allocation)
101101- else:
102102- current_app.logger.warning(f"/admin: capsul {vm['id']} has public_ipv4 {vm['public_ipv4']} which is out of range for its host network {host_id} {network['network_name']} {network['public_ipv4_cidr_block']}")
103103-9090+ host_network_key = f"{host_id}_{network['network_name']}"
9191+ if host_network_key in db_vms_by_host_network:
9292+ for vm in db_vms_by_host_network[host_network_key]:
9393+ ip_address_int = int(ipaddress.ip_address(vm['public_ipv4']))
9494+ if network_start_int <= ip_address_int and ip_address_int <= network_end_int:
9595+ allocation = f"{host_id}_{network['network_name']}_{len(network['allocations'])}"
9696+ inline_styles.append(
9797+ f"""
9898+ .{allocation} {'{'}
9999+ left: {(float(ip_address_int-network_start_int)/network_addresses_width)*network_display_width_px}px;
100100+ width: {network_display_width_px/network_addresses_width}px;
101101+ {'}'}
102102+ """
103103+ )
104104+ network['allocations'].append(allocation)
105105+ else:
106106+ current_app.logger.warning(f"/admin: capsul {vm['id']} has public_ipv4 {vm['public_ipv4']} which is out of range for its host network {host_id} {network['network_name']} {network['public_ipv4_cidr_block']}")
107107+104108 display_hosts.append(display_host)
105109106110107107- # Now creating the capsuls running status ui
111111+ # Now creating the capsul consistency / running status ui
108112 #
109113110110- virt_vms_by_host_and_network = current_app.config["HUB_MODEL"].get_all_by_host_and_network()
111111-112112- # virt_vms_dict = dict()
113113- # for vm in virt_vms:
114114- # virt_vms_dict[vm["id"]] = vm["state"]
114114+ virt_vms_by_id = get_all_vms_from_hosts()
115115116116- # in_db_but_not_in_virt = []
117117- # needs_to_be_started = []
118118- # needs_to_be_started_missing_ipv4 = []
116116+ virt_vm_id_by_ipv4 = dict()
117117+ for vm_id, virt_vm in virt_vms_by_id.items():
118118+ virt_vm_id_by_ipv4[virt_vm['public_ipv4']] = vm_id
119119120120- # for vm in db_vms:
121121- # if vm["id"] not in virt_vms_dict:
122122- # in_db_but_not_in_virt.append(vm["id"])
123123- # elif vm["desired_state"] == "running" and virt_vms_dict[vm["id"]] != "running":
124124- # if vm["id"] in db_vm_by_id:
125125- # needs_to_be_started.append(db_vm_by_id[vm["id"]])
126126- # else:
127127- # needs_to_be_started_missing_ipv4.append(vm["id"])
128128- # elif vm["ipv4"] != current_ipv4
120120+ db_vm_id_by_ipv4 = dict()
121121+ for vm_id, db_vm in db_vms_by_id.items():
122122+ db_vm_id_by_ipv4[db_vm['public_ipv4']] = vm_id
123123+124124+ in_db_but_not_in_virt = []
125125+ state_not_equal_to_desired_state = []
126126+ stole_someone_elses_ip_and_own_ip_avaliable = []
127127+ stole_someone_elses_ip_but_own_ip_also_stolen = []
128128+ has_wrong_ip = []
129129+130130+ for vm_id, db_vm in db_vms_by_id.items():
131131+ if vm_id not in virt_vms_by_id:
132132+ in_db_but_not_in_virt.append(db_vm)
133133+ elif virt_vms_by_id[vm_id]['state'] != db_vm["desired_state"]:
134134+ db_vm["state"] = virt_vms_by_id[vm_id]['state']
135135+ state_not_equal_to_desired_state.append(db_vm)
136136+ elif virt_vms_by_id[vm_id]['public_ipv4'] != db_vm["public_ipv4"]:
137137+ db_vm["desired_ipv4"] = db_vm["public_ipv4"]
138138+ db_vm["current_ipv4"] = virt_vms_by_id[vm_id]['public_ipv4']
139139+ if virt_vms_by_id[vm_id]['public_ipv4'] in db_vm_id_by_ipv4:
140140+ if db_vm["public_ipv4"] not in virt_vm_id_by_ipv4:
141141+ stole_someone_elses_ip_and_own_ip_avaliable.append(db_vm)
142142+ else:
143143+ stole_someone_elses_ip_but_own_ip_also_stolen.append(db_vm)
144144+145145+ has_wrong_ip.append(db_vm)
146146+129147130148 # current_app.logger.info(f"list_of_networks: {json.dumps(list_of_networks)}")
131149···142160 csp_inline_style_nonce=csp_inline_style_nonce,
143161 inline_style='\n'.join(inline_styles),
144162145145- db_vms_by_host_and_network=json.dumps(db_vms_by_host_and_network),
146146- virt_vms_by_host_and_network=json.dumps(virt_vms_by_host_and_network),
163163+ in_db_but_not_in_virt=in_db_but_not_in_virt,
164164+ state_not_equal_to_desired_state=state_not_equal_to_desired_state,
165165+ stole_someone_elses_ip_and_own_ip_avaliable=stole_someone_elses_ip_and_own_ip_avaliable,
166166+ stole_someone_elses_ip_but_own_ip_also_stolen=stole_someone_elses_ip_but_own_ip_also_stolen,
167167+ has_wrong_ip=has_wrong_ip
147168 )
148169149170 response = make_response(response_text)
+41-41
capsulflask/consistency.py
···1010# "host": "baikal",
1111# "network_name": "public1",
1212# "virtual_bridge_name": "virbr1",
1313-# "state": "running"
1313+# "desired_state": "running"
1414# },
1515# { ... },
1616# ...
···41414242 return db_vms_by_id
43434444+4545+# this returns the same shape of object as get_all_vms_from_db except it has 'state' instead of 'desired_state'
4446def get_all_vms_from_hosts() -> dict:
4545- virt_vms = current_app.config["HUB_MODEL"].get_all_by_host_and_network()
4747+ virt_vms_by_host_and_network = current_app.config["HUB_MODEL"].get_all_by_host_and_network()
4648 #virt_networks = current_app.config["HUB_MODEL"].virsh_netlist()
4749 db_hosts = get_model().list_hosts_with_networks(None)
4850···5254 host_id = kv[0]
5355 value = kv[1]
5456 for network in value['networks']:
5555-5757+ if host_id in virt_vms_by_host_and_network and network['network_name'] in virt_vms_by_host_and_network[host_id]:
5858+ for vm in virt_vms_by_host_and_network[host_id][network['network_name']]:
5959+ vm['network_name'] = network['network_name']
6060+ vm['virtual_bridge_name'] = network['virtual_bridge_name']
6161+ vm['host'] = host_id
6262+ virt_vms_by_id[vm['id']] = vm
56635757- for vm in db_vms:
5858- if vm["id"] not in db_vms_by_id:
5959- # TODO
6060- raise Exception("non_deleted_vms_by_host_and_network did not return a vm that was returned by all_vm_ids_with_desired_state")
6161- else:
6262- db_vms_by_id[vm["id"]]["state"] = vm["desired_state"]
6363-6464- virt_vms = current_app.config["HUB_MODEL"].get_vm_()
6464+ return virt_vms_by_id
65656666def ensure_vms_and_db_are_synced():
676768686969-7070- # Now creating the capsuls running status ui
7171- #
6969+ pass
7070+ # # Now creating the capsuls running status ui
7171+ # #
7272737374747575- for vm in db_vms:
7676- db_ids_dict[vm['id']] = vm['desired_state']
7575+ # for vm in db_vms:
7676+ # db_ids_dict[vm['id']] = vm['desired_state']
77777878- for vm in virt_vms:
7979- virt_ids_dict[vm['id']] = vm['desired_state']
7878+ # for vm in virt_vms:
7979+ # virt_ids_dict[vm['id']] = vm['desired_state']
80808181- errors = list()
8181+ # errors = list()
82828383- for id in db_ids_dict:
8484- if id not in virt_ids_dict:
8585- errors.append(f"{id} is in the database but not in the virtualization model")
8686- elif db_ids_dict[id] != virt_ids_dict[id]:
8787- errors.append(f"{id} has the desired state {db_ids_dict[id]} in the database but current state {virt_ids_dict[id]} in the virtualization model")
8383+ # for id in db_ids_dict:
8484+ # if id not in virt_ids_dict:
8585+ # errors.append(f"{id} is in the database but not in the virtualization model")
8686+ # elif db_ids_dict[id] != virt_ids_dict[id]:
8787+ # errors.append(f"{id} has the desired state {db_ids_dict[id]} in the database but current state {virt_ids_dict[id]} in the virtualization model")
88888989- for id in virt_ids_dict:
9090- if id not in db_ids_dict:
9191- errors.append(f"{id} is in the virtualization model but not in the database")
8989+ # for id in virt_ids_dict:
9090+ # if id not in db_ids_dict:
9191+ # errors.append(f"{id} is in the virtualization model but not in the database")
92929393- if len(errors) > 0:
9494- email_addresses_raw = current_app.config['ADMIN_EMAIL_ADDRESSES'].split(",")
9595- email_addresses = list(filter(lambda x: len(x) > 6, map(lambda x: x.strip(), email_addresses_raw ) ))
9393+ # if len(errors) > 0:
9494+ # email_addresses_raw = current_app.config['ADMIN_EMAIL_ADDRESSES'].split(",")
9595+ # email_addresses = list(filter(lambda x: len(x) > 6, map(lambda x: x.strip(), email_addresses_raw ) ))
96969797- current_app.logger.info(f"cron_task: sending inconsistency warning email to {','.join(email_addresses)}:")
9898- for error in errors:
9999- current_app.logger.info(f"cron_task: {error}.")
9797+ # current_app.logger.info(f"cron_task: sending inconsistency warning email to {','.join(email_addresses)}:")
9898+ # for error in errors:
9999+ # current_app.logger.info(f"cron_task: {error}.")
100100101101- current_app.config["FLASK_MAIL_INSTANCE"].send(
102102- Message(
103103- "Capsul Consistency Check Failed",
104104- sender=current_app.config["MAIL_DEFAULT_SENDER"],
105105- body="\n".join(errors),
106106- recipients=email_addresses
107107- )
108108- )101101+ # current_app.config["FLASK_MAIL_INSTANCE"].send(
102102+ # Message(
103103+ # "Capsul Consistency Check Failed",
104104+ # sender=current_app.config["MAIL_DEFAULT_SENDER"],
105105+ # body="\n".join(errors),
106106+ # recipients=email_addresses
107107+ # )
108108+ # )
···184184 else:
185185 current_app.logger.warn(f"get_all_by_host_and_network: '{vm['domain']}' not in vm_state_by_id, defaulting to 'shut off'")
186186187187- vms_by_id[vm['domain']] = dict(macs=dict(), state=vm_state, network=network['name'])
187187+ vms_by_id[vm['domain']] = dict(macs=dict(), state=vm_state, network_name=network['network_name'])
188188189189 vms_by_id[vm['domain']]['macs'][mac] = True
190190···199199 for status in statuses:
200200 if status['mac-address'] in vm_id_by_mac:
201201 vm_id = vm_id_by_mac[status['mac-address']]
202202- vms_by_id[vm_id]['ipv4'] = status['ip-address']
202202+ vms_by_id[vm_id]['public_ipv4'] = status['ip-address']
203203 else:
204204 current_app.logger.warn(f"get_all_by_host_and_network: {status['mac-address']} not in vm_id_by_mac")
205205206206 networks = dict()
207207- for vm_id in vms_by_id:
208208- vm = vms_by_id[vm_id]
209209-207207+ for vm in vms_by_id.values():
210208 if vm['network'] not in networks:
211209 networks[vm['network']] = []
212210
+79-9
capsulflask/templates/admin.html
···36363737 <hr/>
3838 {% endfor %}
3939+</div>
39404141+{% if in_db_but_not_in_virt|length > 0 %}
4242+<div class="third-margin">
4343+ <h1>๐จ in the database but not in the virtualization model ๐จ</h1>
4444+ {% for vm in in_db_but_not_in_virt %}
4045 <div class="row">
4141- <h1>db_vms_by_host_and_network</h1>
4646+ {{vm['id']}} {{vm['public_ipv4']}}
4247 </div>
4848+ {% endfor %}
4949+ <hr/>
5050+</div>
5151+{% endif %}
5252+5353+5454+5555+{% if state_not_equal_to_desired_state|length > 0 %}
5656+<div class="third-margin">
5757+ <h1>๐ด vm state != desired state ๐ด</h1>
5858+ {% for vm in state_not_equal_to_desired_state %}
4359 <div class="row">
4444- <pre>
4545-{{db_vms_by_host_and_network}}
4646- </pre>
6060+ <div>{{vm['id']}}: state={{vm['state']}} desired_state={{vm['desired_state']}}</div>
6161+ <form method="post">
6262+ <input type="hidden" name="action" value="set_state"></input>
6363+ <input type="hidden" name="id" value="{{vm['id']}}"></input>
6464+ <input type="hidden" name="state" value="{{vm['desired_state']}}"></input>
6565+ <input type="hidden" name="csrf-token" value="{{ csrf_token }}"/>
6666+ <input type="submit" value="๐ฆ START/STOP"/>
6767+ </form>
4768 </div>
6969+ {% endfor %}
7070+ <hr/>
7171+</div>
7272+{% endif %}
48737474+7575+7676+{% if stole_someone_elses_ip_and_own_ip_avaliable|length > 0 %}
7777+<div class="third-margin">
7878+ <h1>๐ป stole someone elses ip and own desired ip is avaliable ๐ป</h1>
7979+ {% for vm in stole_someone_elses_ip_and_own_ip_avaliable %}
4980 <div class="row">
5050- <h1>virt_vms_by_host_and_network</h1>
8181+ <div>{{vm['id']}}: current_ipv4={{vm['current_ipv4']}} desired_ipv4={{vm['desired_ipv4']}}</div>
8282+ <form method="post">
8383+ <input type="hidden" name="action" value="dhcp_reset"></input>
8484+ <input type="hidden" name="id" value="{{vm['id']}}"></input>
8585+ <input type="hidden" name="csrf-token" value="{{ csrf_token }}"/>
8686+ <input type="submit" value="๐จ DHCP RESET"/>
8787+ </form>
5188 </div>
8989+ {% endfor %}
9090+ <hr/>
9191+</div>
9292+{% endif %}
9393+9494+{% if has_wrong_ip|length > 0 %}
9595+<div class="third-margin">
9696+ <h1>๐ฅด has wrong ip address ๐ฅด</h1>
9797+ {% for vm in has_wrong_ip %}
5298 <div class="row">
5353- <pre>
5454-{{virt_vms_by_host_and_network}}
5555- </pre>
9999+ <div>{{vm['id']}}: current_ipv4={{vm['current_ipv4']}} desired_ipv4={{vm['desired_ipv4']}}</div>
100100+ <form method="post">
101101+ <input type="hidden" name="action" value="dhcp_reset"></input>
102102+ <input type="hidden" name="id" value="{{vm['id']}}"></input>
103103+ <input type="hidden" name="csrf-token" value="{{ csrf_token }}"/>
104104+ <input type="submit" value="๐จ DHCP RESET"/>
105105+ </form>
56106 </div>
107107+ {% endfor %}
108108+ <hr/>
109109+</div>
110110+{% endif %}
57111581125959-113113+{% if stole_someone_elses_ip_but_own_ip_also_stolen|length > 0 %}
114114+<div class="third-margin">
115115+ <h1>๐ stole someone elses ip but own desired ip was also stolen ๐</h1>
116116+ {% for vm in stole_someone_elses_ip_but_own_ip_also_stolen %}
117117+ <div class="row">
118118+ <div>{{vm['id']}}: current_ipv4={{vm['current_ipv4']}} desired_ipv4={{vm['desired_ipv4']}}</div>
119119+ <form method="post">
120120+ <input type="hidden" name="action" value="stop_and_expire"></input>
121121+ <input type="hidden" name="id" value="{{vm['id']}}"></input>
122122+ <input type="hidden" name="csrf-token" value="{{ csrf_token }}"/>
123123+ <input type="submit" value="๐ STOP AND EXPIRE DHCP LEASE"/>
124124+ </form>
125125+ </div>
126126+ {% endfor %}
127127+ <hr/>
60128</div>
129129+{% endif %}
6113062131<div class="third-margin">
63132 <div class="row">
···74143 </div>
7514476145</div>
146146+77147{% endblock %}
7814879149{% block pagesource %}/templates/admin.html{% endblock %}