Skip to content

Commit be2e7b5

Browse files
authored
Merge pull request #372 from fabric-testbed/audit
Audit and cleanup consistencies
2 parents a625959 + 2e5ea0a commit be2e7b5

File tree

2 files changed

+147
-5
lines changed

2 files changed

+147
-5
lines changed

tools/audit.py

Lines changed: 145 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
# Author: Komal Thareja ([email protected])
2626
import argparse
2727
import logging
28+
import os
29+
import re
2830
import traceback
2931
from datetime import datetime, timezone, timedelta
3032
from logging.handlers import RotatingFileHandler
@@ -50,10 +52,15 @@ class MainClass:
5052
- Remove/Delete slices older than specified number of days
5153
- Remove/Delete dangling network services which connect the ports to deleted/closed VMs
5254
"""
53-
def __init__(self, config_file: str):
55+
def __init__(self, config_file: str, am_config_file: str):
56+
self.am_config_dict = None
5457
with open(config_file) as f:
5558
config_dict = yaml.safe_load(f)
5659

60+
if am_config_file is not None and os.path.exists(am_config_file):
61+
with open(am_config_file) as f:
62+
self.am_config_dict = yaml.safe_load(f)
63+
5764
# Load the config file
5865
self.log_config = config_dict[Constants.CONFIG_LOGGING_SECTION]
5966

@@ -215,6 +222,132 @@ def delete_dead_closing_slice(self, *, days: int):
215222
self.logger.error(f"Failed to delete slice: {s.get_slice_id()}: e: {e}")
216223
self.logger.error(traceback.format_exc())
217224

225+
def execute_ansible(self, *, inventory_path: str, playbook_path: str, extra_vars: dict,
226+
ansible_python_interpreter: str, sources: str = None, private_key_file: str = None,
227+
host_vars: dict = None, host: str = None, user: str = None):
228+
from fabric_am.util.ansible_helper import AnsibleHelper
229+
ansible_helper = AnsibleHelper(inventory_path=inventory_path, logger=self.logger,
230+
ansible_python_interpreter=ansible_python_interpreter,
231+
sources=sources)
232+
233+
ansible_helper.set_extra_vars(extra_vars=extra_vars)
234+
235+
if host is not None and host_vars is not None and len(host_vars) > 0:
236+
for key, value in host_vars.items():
237+
ansible_helper.add_vars(host=host, var_name=key, value=value)
238+
239+
self.logger.info(f"Executing playbook {playbook_path} extra_vars: {extra_vars} host_vars: {host_vars}")
240+
ansible_helper.run_playbook(playbook_path=playbook_path, private_key_file=private_key_file, user=user)
241+
return ansible_helper.get_result_callback()
242+
243+
def clean_sliver_inconsistencies(self):
244+
try:
245+
actor_type = self.actor_config[Constants.TYPE]
246+
if actor_type.lower() != ActorType.Authority.name.lower() or self.am_config_dict is None:
247+
return
248+
249+
from fabric_am.util.am_constants import AmConstants
250+
pb_section = self.am_config_dict.get(AmConstants.PLAYBOOK_SECTION)
251+
if pb_section is None:
252+
return
253+
inventory_location = pb_section.get(AmConstants.PB_INVENTORY)
254+
pb_dir = pb_section.get(AmConstants.PB_LOCATION)
255+
vm_playbook_name = pb_section.get("VM")
256+
if inventory_location is None or pb_dir is None or vm_playbook_name is None:
257+
return
258+
259+
vm_playbook_path = f"{pb_dir}/{vm_playbook_name}"
260+
261+
ansible_python_interpreter = None
262+
ansible_section = self.am_config_dict.get(AmConstants.ANSIBLE_SECTION)
263+
if ansible_section:
264+
ansible_python_interpreter = ansible_section.get(AmConstants.ANSIBLE_PYTHON_INTERPRETER)
265+
266+
actor_db = ActorDatabase(user=self.database_config[Constants.PROPERTY_CONF_DB_USER],
267+
password=self.database_config[Constants.PROPERTY_CONF_DB_PASSWORD],
268+
database=self.database_config[Constants.PROPERTY_CONF_DB_NAME],
269+
db_host=self.database_config[Constants.PROPERTY_CONF_DB_HOST],
270+
logger=self.logger)
271+
272+
states = [ReservationStates.Active.value,
273+
ReservationStates.ActiveTicketed.value,
274+
ReservationStates.Ticketed.value,
275+
ReservationStates.Nascent.value]
276+
277+
resource_type = ["VM"]
278+
279+
# Get the Active Slivers from CF
280+
slivers = actor_db.get_reservations(states=states, rsv_type=resource_type)
281+
cf_active_sliver_ids = []
282+
if slivers:
283+
for s in slivers:
284+
cf_active_sliver_ids.append(str(s.get_reservation_id()))
285+
286+
self.logger.info(f"Active Slivers: {cf_active_sliver_ids}")
287+
288+
# Get the VMs from Openstack
289+
result_callback_1 = self.execute_ansible(inventory_path=inventory_location,
290+
playbook_path=vm_playbook_path,
291+
extra_vars={"operation": "list"},
292+
ansible_python_interpreter=ansible_python_interpreter)
293+
result_1 = result_callback_1.get_json_result_ok()
294+
295+
os_vms = {}
296+
if result_1 and result_1.get('openstack_servers'):
297+
servers = result_1.get('openstack_servers')
298+
for s in servers:
299+
if s.get('OS-EXT-SRV-ATTR:instance_name') and s.get('name'):
300+
os_vms[s.get('OS-EXT-SRV-ATTR:instance_name')] = s.get('name')
301+
302+
uuid_pattern = r'([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})'
303+
304+
# Cleanup inconsistencies between CF and Open Stack
305+
if len(cf_active_sliver_ids):
306+
for instance, vm_name in os_vms.items():
307+
try:
308+
# Search for UUID in the input string
309+
match = re.search(uuid_pattern, vm_name)
310+
311+
# Extract UUID if found
312+
if match:
313+
sliver_id = match.group(1)
314+
if sliver_id not in cf_active_sliver_ids:
315+
result_2 = self.execute_ansible(inventory_path=inventory_location,
316+
playbook_path=vm_playbook_path,
317+
extra_vars={"operation": "delete", "vmname": vm_name},
318+
ansible_python_interpreter=ansible_python_interpreter)
319+
self.logger.info(f"Deleted instance: {vm_name}; result: {result_2.get_json_result_ok()}")
320+
else:
321+
self.logger.error(f"Sliver Id not found in the input string: {vm_name}")
322+
except Exception as e:
323+
self.logger.error(f"Failed to cleanup CF and openstack inconsistencies instance: {instance} vm: {vm_name}: {e}")
324+
self.logger.error(traceback.format_exc())
325+
326+
# Cleanup inconsistencies between Open Stack and Virsh
327+
result_3 = self.execute_ansible(inventory_path=inventory_location,
328+
playbook_path=f"{pb_dir}/worker_libvirt_operations.yml",
329+
extra_vars={"operation": "listall"},
330+
ansible_python_interpreter=ansible_python_interpreter)
331+
332+
for host, ok_result in result_3.host_ok.items():
333+
try:
334+
if ok_result and ok_result._result:
335+
virsh_vms = ok_result._result.get('stdout_lines', [])
336+
self.logger.info(f"Host: {host} has VMs: {virsh_vms}")
337+
for instance in virsh_vms:
338+
if instance not in os_vms:
339+
results_4 = self.execute_ansible(inventory_path=inventory_location,
340+
playbook_path=vm_playbook_path,
341+
extra_vars={"operation": "delete", "host": str(host)},
342+
ansible_python_interpreter=ansible_python_interpreter)
343+
self.logger.info(f"Deleted instance: {instance}; result: {results_4.get_json_result_ok()}")
344+
except Exception as e:
345+
self.logger.error(f"Failed to cleanup openstack and virsh inconsistencies on {host}: {e}")
346+
self.logger.error(traceback.format_exc())
347+
except Exception as e:
348+
self.logger.error(f"Failed to cleanup inconsistencies: {e}")
349+
self.logger.error(traceback.format_exc())
350+
218351
def handle_command(self, args):
219352
"""
220353
Command Handler
@@ -230,8 +363,15 @@ def handle_command(self, args):
230363
# Slivers
231364
elif args.command == "slivers":
232365
# Close operation
233-
if args.operation is not None and args.operation == "close":
234-
self.delete_dangling_network_slivers()
366+
if args.operation is not None and args.operation == "cleanup":
367+
self.clean_sliver_inconsistencies()
368+
else:
369+
print(f"Unsupported operation: {args.operation}")
370+
elif args.command == "audit":
371+
# Close operation
372+
if args.operation is not None and args.operation == "audit":
373+
self.delete_dead_closing_slice(days=args.days)
374+
self.clean_sliver_inconsistencies()
235375
else:
236376
print(f"Unsupported operation: {args.operation}")
237377
else:
@@ -240,12 +380,13 @@ def handle_command(self, args):
240380

241381
if __name__ == '__main__':
242382
parser = argparse.ArgumentParser()
383+
parser.add_argument("-a", dest='amconfig', required=True, type=str)
243384
parser.add_argument("-f", dest='config', required=True, type=str)
244385
parser.add_argument("-d", dest='days', required=False, type=int, default=30)
245386
parser.add_argument("-c", dest='command', required=True, type=str)
246387
parser.add_argument("-o", dest='operation', required=True, type=str)
247388
args = parser.parse_args()
248389

249-
mc = MainClass(config_file=args.config)
390+
mc = MainClass(config_file=args.config, am_config_file=args.amconfig)
250391
mc.handle_command(args)
251392

tools/install.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#!/usr/bin/sh
22

3-
echo "0 2 * * * root /usr/local/bin/python3.11 /usr/src/app/audit.py -f /etc/fabric/actor/config/config.yaml -d 30 -c slices -o remove" >> /etc/crontab
3+
echo "0 * * * * root /usr/local/bin/python3.11 /usr/src/app/audit.py -f /etc/fabric/actor/config/config.yaml -a /etc/fabric/actor/config/vm_handler_config.yml -d 30 -c audit -o audit" >> /etc/crontab
4+
#echo "0 2 * * * root /usr/local/bin/python3.11 /usr/src/app/audit.py -f /etc/fabric/actor/config/config.yaml -d 30 -c slices -o remove" >> /etc/crontab
45
#echo "*/15 * * * * root /usr/local/bin/python3.11 /usr/src/app/audit.py -f /etc/fabric/actor/config/config.yaml -c slivers -o close" >> /etc/crontab
56
service cron reload
67
service cron restart

0 commit comments

Comments
 (0)