25
25
# Author: Komal Thareja ([email protected] )
26
26
import argparse
27
27
import logging
28
+ import os
29
+ import re
28
30
import traceback
29
31
from datetime import datetime , timezone , timedelta
30
32
from logging .handlers import RotatingFileHandler
@@ -50,10 +52,15 @@ class MainClass:
50
52
- Remove/Delete slices older than specified number of days
51
53
- Remove/Delete dangling network services which connect the ports to deleted/closed VMs
52
54
"""
53
- def __init__ (self , config_file : str ):
55
+ def __init__ (self , config_file : str , am_config_file : str ):
56
+ self .am_config_dict = None
54
57
with open (config_file ) as f :
55
58
config_dict = yaml .safe_load (f )
56
59
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
+
57
64
# Load the config file
58
65
self .log_config = config_dict [Constants .CONFIG_LOGGING_SECTION ]
59
66
@@ -215,6 +222,132 @@ def delete_dead_closing_slice(self, *, days: int):
215
222
self .logger .error (f"Failed to delete slice: { s .get_slice_id ()} : e: { e } " )
216
223
self .logger .error (traceback .format_exc ())
217
224
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
+
218
351
def handle_command (self , args ):
219
352
"""
220
353
Command Handler
@@ -230,8 +363,15 @@ def handle_command(self, args):
230
363
# Slivers
231
364
elif args .command == "slivers" :
232
365
# 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 ()
235
375
else :
236
376
print (f"Unsupported operation: { args .operation } " )
237
377
else :
@@ -240,12 +380,13 @@ def handle_command(self, args):
240
380
241
381
if __name__ == '__main__' :
242
382
parser = argparse .ArgumentParser ()
383
+ parser .add_argument ("-a" , dest = 'amconfig' , required = True , type = str )
243
384
parser .add_argument ("-f" , dest = 'config' , required = True , type = str )
244
385
parser .add_argument ("-d" , dest = 'days' , required = False , type = int , default = 30 )
245
386
parser .add_argument ("-c" , dest = 'command' , required = True , type = str )
246
387
parser .add_argument ("-o" , dest = 'operation' , required = True , type = str )
247
388
args = parser .parse_args ()
248
389
249
- mc = MainClass (config_file = args .config )
390
+ mc = MainClass (config_file = args .config , am_config_file = args . amconfig )
250
391
mc .handle_command (args )
251
392
0 commit comments