Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 147 additions & 0 deletions src/aleph/vm/garbage_collector.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
"""Free disk space by removing unused volume from the hard drive to free up


This script allow to manually list and remove volume linked to inactive VM
It fetches data from the scheduler and pyaleph main's node as to fetch information on the status of the VM.
Then display them to the user to determine if they can be removed safely.

Requires to be run as root.
"""

import os
import subprocess
from pathlib import Path

Check warning on line 13 in src/aleph/vm/garbage_collector.py

View check run for this annotation

Codecov / codecov/patch

src/aleph/vm/garbage_collector.py#L11-L13

Added lines #L11 - L13 were not covered by tests

import requests

Check warning on line 15 in src/aleph/vm/garbage_collector.py

View check run for this annotation

Codecov / codecov/patch

src/aleph/vm/garbage_collector.py#L15

Added line #L15 was not covered by tests

# following hashes are used in tests or debug VM, we can ignore them.
TEST_HASHES = [

Check warning on line 18 in src/aleph/vm/garbage_collector.py

View check run for this annotation

Codecov / codecov/patch

src/aleph/vm/garbage_collector.py#L18

Added line #L18 was not covered by tests
"fake_vm_fake_vm_fake_vm_fake_vm_fake_vm_fake_vm_fake_vm_fake_vm_",
"cafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafe",
"decadecadecadecadecadecadecadecadecadecadecadecadecadecadecadeca",
"63faf8b5db1cf8d965e6a464a0cb8062af8e7df131729e48738342d956f29ace",
"67705389842a0a1b95eaa408b009741027964edc805997475e95c505d642edd8",
]

api_server = [

Check warning on line 26 in src/aleph/vm/garbage_collector.py

View check run for this annotation

Codecov / codecov/patch

src/aleph/vm/garbage_collector.py#L26

Added line #L26 was not covered by tests
"https://api1.aleph.im",
"https://api2.aleph.im",
"https://api3.aleph.im",
# 'https://official.aleph.cloud',
]

endpoint = "/api/v0/messages/"

Check warning on line 33 in src/aleph/vm/garbage_collector.py

View check run for this annotation

Codecov / codecov/patch

src/aleph/vm/garbage_collector.py#L33

Added line #L33 was not covered by tests


def check_api(item_hash):

Check warning on line 36 in src/aleph/vm/garbage_collector.py

View check run for this annotation

Codecov / codecov/patch

src/aleph/vm/garbage_collector.py#L36

Added line #L36 was not covered by tests
"""Check on which api the ITEM_HASH msg is available."""
for api in api_server:
response = requests.get(api + endpoint + item_hash)
print(api + " ", end="")
print(response.status_code, end="")
j = response.json()
print(" " + j["status"], end="")
print()

Check warning on line 44 in src/aleph/vm/garbage_collector.py

View check run for this annotation

Codecov / codecov/patch

src/aleph/vm/garbage_collector.py#L39-L44

Added lines #L39 - L44 were not covered by tests


p = Path("/var/lib/aleph/vm/volumes/persistent")

Check warning on line 47 in src/aleph/vm/garbage_collector.py

View check run for this annotation

Codecov / codecov/patch

src/aleph/vm/garbage_collector.py#L47

Added line #L47 was not covered by tests
# print current size
os.system(" ".join(["df", "-h", str(p)]))

Check warning on line 49 in src/aleph/vm/garbage_collector.py

View check run for this annotation

Codecov / codecov/patch

src/aleph/vm/garbage_collector.py#L49

Added line #L49 was not covered by tests

# Before anything check that we can reach the api server and the scheduler server
res = requests.get("https://api2.aleph.im/api/v0/info/public.json")
assert res.status_code == 200
res = requests.get("https://scheduler.api.aleph.cloud/api/v0/plan")
assert res.status_code == 200

Check warning on line 55 in src/aleph/vm/garbage_collector.py

View check run for this annotation

Codecov / codecov/patch

src/aleph/vm/garbage_collector.py#L52-L55

Added lines #L52 - L55 were not covered by tests

volume_dirs = list(p.glob("*"))

Check warning on line 57 in src/aleph/vm/garbage_collector.py

View check run for this annotation

Codecov / codecov/patch

src/aleph/vm/garbage_collector.py#L57

Added line #L57 was not covered by tests
for i, f in enumerate(reversed(volume_dirs)):
if not f.is_dir():
continue
item_hash = f.name
print(f"= {i}/{len(volume_dirs) -1} {item_hash}")

Check warning on line 62 in src/aleph/vm/garbage_collector.py

View check run for this annotation

Codecov / codecov/patch

src/aleph/vm/garbage_collector.py#L60-L62

Added lines #L60 - L62 were not covered by tests
if item_hash in TEST_HASHES:
print("Test VM, skipping")
continue

Check warning on line 65 in src/aleph/vm/garbage_collector.py

View check run for this annotation

Codecov / codecov/patch

src/aleph/vm/garbage_collector.py#L64-L65

Added lines #L64 - L65 were not covered by tests

res = requests.get(f"https://api2.aleph.im/api/v0/messages/{item_hash}")

Check warning on line 67 in src/aleph/vm/garbage_collector.py

View check run for this annotation

Codecov / codecov/patch

src/aleph/vm/garbage_collector.py#L67

Added line #L67 was not covered by tests

if res.status_code == 404:
print("Not found on API server")
continue
message = res.json()
message_status = message.get("status")

Check warning on line 73 in src/aleph/vm/garbage_collector.py

View check run for this annotation

Codecov / codecov/patch

src/aleph/vm/garbage_collector.py#L70-L73

Added lines #L70 - L73 were not covered by tests
# if message_status == "forgotten" or message_status == "rejected":
# print(f"{item_hash} status: {j.message_status('status')}")
# continue
# print(f"{item_hash} status: {j.message_status('status')}")
sender = message["message"]["sender"]
print(f"Sender {sender}. State: {message_status}")

Check warning on line 79 in src/aleph/vm/garbage_collector.py

View check run for this annotation

Codecov / codecov/patch

src/aleph/vm/garbage_collector.py#L78-L79

Added lines #L78 - L79 were not covered by tests
if not message["message"]["type"] == "INSTANCE":
print("Type: ", message["message"]["type"], "not an instance")
continue
scheduler_res = requests.get(f"https://scheduler.api.aleph.cloud/api/v0/allocation/{item_hash}")
schedule = None

Check warning on line 84 in src/aleph/vm/garbage_collector.py

View check run for this annotation

Codecov / codecov/patch

src/aleph/vm/garbage_collector.py#L81-L84

Added lines #L81 - L84 were not covered by tests

if scheduler_res.status_code == 404:
print("Not found on scheduler plan")

Check warning on line 87 in src/aleph/vm/garbage_collector.py

View check run for this annotation

Codecov / codecov/patch

src/aleph/vm/garbage_collector.py#L87

Added line #L87 was not covered by tests
else:
schedule = scheduler_res.json()
print(f"scheduled on {schedule['node']['node_id']}")

Check warning on line 90 in src/aleph/vm/garbage_collector.py

View check run for this annotation

Codecov / codecov/patch

src/aleph/vm/garbage_collector.py#L89-L90

Added lines #L89 - L90 were not covered by tests

balance = requests.get(f"https://api2.aleph.im/api/v0/addresses/{sender}/balance").json()
print(f"User balance: {balance['balance']:.2f}, locked amount {balance['locked_amount']:.2f}")

Check warning on line 93 in src/aleph/vm/garbage_collector.py

View check run for this annotation

Codecov / codecov/patch

src/aleph/vm/garbage_collector.py#L92-L93

Added lines #L92 - L93 were not covered by tests
# print(balance)

# check if process is still running

proc_ret = subprocess.run(

Check warning on line 98 in src/aleph/vm/garbage_collector.py

View check run for this annotation

Codecov / codecov/patch

src/aleph/vm/garbage_collector.py#L98

Added line #L98 was not covered by tests
f"systemctl status aleph-vm-controller@{item_hash}.service --no-pager",
shell=True,
capture_output=True,
)
exit_code = proc_ret.returncode

Check warning on line 103 in src/aleph/vm/garbage_collector.py

View check run for this annotation

Codecov / codecov/patch

src/aleph/vm/garbage_collector.py#L103

Added line #L103 was not covered by tests
if exit_code == 0:
proc_status = "running"

Check warning on line 105 in src/aleph/vm/garbage_collector.py

View check run for this annotation

Codecov / codecov/patch

src/aleph/vm/garbage_collector.py#L105

Added line #L105 was not covered by tests
elif exit_code == 3:
proc_status = "stopped"

Check warning on line 107 in src/aleph/vm/garbage_collector.py

View check run for this annotation

Codecov / codecov/patch

src/aleph/vm/garbage_collector.py#L107

Added line #L107 was not covered by tests
else:
proc_status = "error"
print("Unknown process state", exit_code)

Check warning on line 110 in src/aleph/vm/garbage_collector.py

View check run for this annotation

Codecov / codecov/patch

src/aleph/vm/garbage_collector.py#L109-L110

Added lines #L109 - L110 were not covered by tests
# to remove

if proc_status != "running":
# not running and forgotten

if message_status == "forgotten" or message_status == "rejected":
print("Recommendation: remove, process not running and message rejected or forgotten")

Check warning on line 117 in src/aleph/vm/garbage_collector.py

View check run for this annotation

Codecov / codecov/patch

src/aleph/vm/garbage_collector.py#L117

Added line #L117 was not covered by tests
else:
print("Process stopped")

Check warning on line 119 in src/aleph/vm/garbage_collector.py

View check run for this annotation

Codecov / codecov/patch

src/aleph/vm/garbage_collector.py#L119

Added line #L119 was not covered by tests
# print(f"balances: {balance['balance']}, locked amount {balance['locked_amount']}'")

while True:
inp = input("Do you want to delete y/n ? More info (h) [n] ").lower()

Check warning on line 123 in src/aleph/vm/garbage_collector.py

View check run for this annotation

Codecov / codecov/patch

src/aleph/vm/garbage_collector.py#L122-L123

Added lines #L122 - L123 were not covered by tests
if inp in ["y", "yes"]:
os.system(f"dmsetup remove {item_hash}_base")
os.system(f"dmsetup remove {item_hash}_rootfs")
os.system(f"rm -r {f.absolute()}")

Check warning on line 127 in src/aleph/vm/garbage_collector.py

View check run for this annotation

Codecov / codecov/patch

src/aleph/vm/garbage_collector.py#L125-L127

Added lines #L125 - L127 were not covered by tests
# close all loop device
os.system(

Check warning on line 129 in src/aleph/vm/garbage_collector.py

View check run for this annotation

Codecov / codecov/patch

src/aleph/vm/garbage_collector.py#L129

Added line #L129 was not covered by tests
"sudo losetup -l | grep 'persistent' | grep deleted | awk '{print $1}' | sudo xargs losetup -d {}"
)
break

Check warning on line 132 in src/aleph/vm/garbage_collector.py

View check run for this annotation

Codecov / codecov/patch

src/aleph/vm/garbage_collector.py#L132

Added line #L132 was not covered by tests
elif inp == "h":
print(proc_ret.stdout.decode())
check_api(item_hash)
print(f"https://api2.aleph.im/api/v0/messages/{item_hash}")
print(f"https://api2.aleph.im/api/v0/addresses/{sender}/balance")

Check warning on line 137 in src/aleph/vm/garbage_collector.py

View check run for this annotation

Codecov / codecov/patch

src/aleph/vm/garbage_collector.py#L134-L137

Added lines #L134 - L137 were not covered by tests
else:
break

Check warning on line 139 in src/aleph/vm/garbage_collector.py

View check run for this annotation

Codecov / codecov/patch

src/aleph/vm/garbage_collector.py#L139

Added line #L139 was not covered by tests

else:
print("process is running, do not delete")

Check warning on line 142 in src/aleph/vm/garbage_collector.py

View check run for this annotation

Codecov / codecov/patch

src/aleph/vm/garbage_collector.py#L142

Added line #L142 was not covered by tests


# print current size.
print("Size after")
os.system(" ".join(["df", "-h", str(p)]))

Check warning on line 147 in src/aleph/vm/garbage_collector.py

View check run for this annotation

Codecov / codecov/patch

src/aleph/vm/garbage_collector.py#L146-L147

Added lines #L146 - L147 were not covered by tests