3 # node manager has a few working assumptions, like
4 # if a domain d does not exist, there is no /vservers/d
6 # this utility tries to detect and assess potentially
7 # conflictual situations, that could prevent nodemanager
8 # from recovering properly
10 # the logic is simply to find zombie-containers, i.e.
11 # VMs that do have a workdir in /vservers/<zombie>
12 # but that are not reported as running by virsh --list
13 # which suggests they have been improperly trashed
16 # then we trash them but for that some subdirs must be
17 # btrfs-subvolume-delete'd and not rm-rf'ed
23 from argparse import ArgumentParser
25 def running_domains(all_domains=False):
34 command.append('--all')
35 names_string = subprocess.check_output(
37 universal_newlines = True,
38 stdin = subprocess.DEVNULL,
40 names = [ name for name in names_string.strip().split("\n") if name ]
43 def existing_vservers():
44 all_dirs = glob.glob("/vservers/*")
45 dirs = ( dir for dir in all_dirs if os.path.isdir(dir) )
46 dirnames = ( path.replace("/vservers/", "") for path in dirs)
49 def display_or_run_commands(commands, run):
52 print("---- You should run")
53 for command in commands:
54 print(" ".join(command))
56 for command in commands:
57 print("Running {}".format(" ".join(command)))
58 retcod = subprocess.call(command)
60 print("Warning: failed with retcod = {}".format(retcod))
63 parser = ArgumentParser()
64 # the default is to cowardly show commands to run
65 # use --run to actually do it
66 parser.add_argument("-r", "--run", action='store_true', default=False,
67 help="actually run commands, that otherwise are just displayed")
68 parser.add_argument("-d", "--deep", action='store_true', default=False,
69 help="spot and destroy containers that are known to libvirt, but not running")
70 parser.add_argument("-v", "--verbose",
71 help="also displays variable definitions to cut-and-paste for the shell")
72 args = parser.parse_args()
74 known_containers = set(running_domains(all_domains=True))
75 running_containers = set(running_domains())
76 not_running_containers = known_containers - running_containers
80 print("Found {} containers that are known but not running".format(len(not_running_containers)))
81 for not_running_container in not_running_containers:
82 commands.append(['userdel', not_running_container])
83 commands.append(['virsh', '-c', 'lxc:///', 'undefine', not_running_container])
84 display_or_run_commands(commands, args.run)
86 existing_containers = set(existing_vservers())
87 zombies_containers = existing_containers - running_containers
89 # the prefix used to locate subvolumes
97 # we need to call 'btrfs subvolume delete' on these remainings
98 # instead of just 'rm'
99 if zombies_containers:
100 print("-------- Found {} existing, but not running, containers".format(len(zombies_containers)))
102 zombie_dirs = ["/vservers/"+z for z in zombies_containers]
104 print("zombie_dirs='{}'".format(" ".join(zombie_dirs)))
106 for z in zombies_containers
107 for prefix in flavour_prefixes
108 for path in glob.glob("/vservers/{z}/{prefix}*".format(z=z, prefix=prefix))]
111 print("zombie_subvolumes='{}'".format(" ".join(subvolumes)))
112 for subvolume in subvolumes:
113 commands.append([ 'btrfs', 'subvolume', 'delete', subvolume])
114 for zombie_dir in zombie_dirs:
115 commands.append([ 'btrfs', 'subvolume', 'delete', zombie_dir ])
116 display_or_run_commands(commands, args.run)
117 # find the containers dirs that might still exist
118 zombie_dirs = [ path for path in zombie_dirs if os.path.isdir(path) ]
119 commands = [ ['rm', '-rf', path] for path in zombie_dirs ]
120 display_or_run_commands(commands, args.run)
122 #### should happen much less frequently
123 weirdos_containers = running_containers - existing_containers
124 if weirdos_containers:
125 print("-------- Found {} running but non existing".format(len(weirdos_containers)))
126 for w in weirdos_containers:
127 print("/vservers/{}".format(w))
129 print("{} slices are currently running".format(len(running_containers)))