Skip to content

Instantly share code, notes, and snippets.

@kagesenshi
Last active September 23, 2023 13:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kagesenshi/889facccbbb314c1775dd6ca5af3062a to your computer and use it in GitHub Desktop.
Save kagesenshi/889facccbbb314c1775dd6ca5af3062a to your computer and use it in GitHub Desktop.
Libvirt Auto Memory Ballooning
#!/usr/bin/python3
from subprocess import Popen, PIPE
def start_monitor(domain, interval: int = 10):
proc = Popen(['virsh','dommemstat', domain, '--period', str(interval)],
stdout=PIPE)
proc.wait()
def get_domains():
proc = Popen(['virsh','list','--name'],stdout=PIPE)
proc.wait()
out = proc.stdout.read().decode('utf8').strip().split('\n')
return [o for o in out if o.strip()]
def get_memory_usage(domain):
proc = Popen(['virsh', 'dommemstat', domain], stdout=PIPE)
proc.wait()
out = proc.stdout.read().decode('utf8').strip().split('\n')
res = dict([v.split() for v in out])
for k in res:
res[k] = int(res[k])
return res
def get_memory_usage(domain):
proc = Popen(['virsh', 'dommemstat', domain], stdout=PIPE)
proc.wait()
out = proc.stdout.read().decode('utf8').strip().split('\n')
res = dict([v.split() for v in out])
for k in res:
res[k] = int(res[k])
return res
def get_dominfo(domain):
proc = Popen(['virsh', 'dominfo', domain], stdout=PIPE)
proc.wait()
out = proc.stdout.read().decode('utf8').strip().split('\n')
res = {}
for v in out:
vv = v.split(':')
res[vv[0].strip()] = ':'.join(vv[1:]).strip()
return res
def balloon(domain, target):
proc = Popen(['virsh', 'setmem', domain, '--size', str(target), '--current'], stdout=PIPE)
proc.wait()
out = proc.stdout.read().decode('utf8').strip().split('\n')
i1G=1048576
i512M=524288
i256M=262144
STEP=i1G
res = {}
for d in get_domains():
start_monitor(d)
m = get_memory_usage(d)
if 'unused' not in m:
continue
dominfo = get_dominfo(d)
available = int(dominfo['Max memory'].split()[0])
unused = m['unused']
actual = m['actual']
# negative means not enough memory, positive means enough memory
diff = (unused/available * 100)
targets = range(i1G, available+(STEP*3), STEP)
target = actual
if target <= i1G:
target = i1G
if diff < 20:
if actual > i1G:
target = actual + STEP
elif diff > 40:
if (actual - STEP) > i1G:
target = actual - STEP
for i,t in enumerate(targets):
next_target = target
if i+1 < len(targets):
next_target = targets[i+1]
if target <= t and t <= next_target:
target = t
break
if target > available:
target = available
if target != actual:
print('Free Mem (%d%%) Ballooning %s %dM->%dM ' % (diff, d, actual/1024, target/1024))
balloon(d, target)
res[d] = m
[Unit]
Description=Libvirt memory auto ballooning
[Service]
Type=oneshot
ExecStart=/usr/bin/python3 /opt/auto-ballooning.py
# vim: set ft=ini:
[Unit]
Description=Libvirt auto memory ballooning
[Timer]
OnCalendar=*:5/10
Persistent=true
[Install]
WantedBy=timers.target
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment