Disks Service
This commit is contained in:
parent
271007044a
commit
7f5b5209a1
10 changed files with 256 additions and 0 deletions
31
backend/disks/disk.py
Normal file
31
backend/disks/disk.py
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
import pathlib
|
||||
import threading
|
||||
import weakref
|
||||
|
||||
import disk_paths
|
||||
import disk_utils
|
||||
|
||||
DICTIONARY_LOCK = threading.Lock()
|
||||
DISK_LOCKS = weakref.WeakValueDictionary()
|
||||
|
||||
def create_disk(request):
|
||||
paths = disk_paths.DiskPaths(request)
|
||||
workstation = request['workstation']
|
||||
|
||||
with disk_lock(paths):
|
||||
if not paths.target_path.exists():
|
||||
disk_utils.create_os_disk(paths)
|
||||
|
||||
if workstation['cpu'] == 'ARM' and not paths.nvram_target_path.exists():
|
||||
disk_utils.create_nvram_disk(paths)
|
||||
|
||||
def delete_disk(request):
|
||||
paths = disk_paths.DiskPaths(request)
|
||||
|
||||
with disk_lock(paths):
|
||||
paths.target_path.unlink(missing_ok=True)
|
||||
paths.nvram_target_path.unlink(missing_ok=True)
|
||||
|
||||
def disk_lock(paths):
|
||||
with DICTIONARY_LOCK:
|
||||
return DISK_LOCKS.setdefault(paths.target_filename, threading.Lock())
|
||||
60
backend/disks/disk_paths.py
Normal file
60
backend/disks/disk_paths.py
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
import pathlib
|
||||
|
||||
class DiskPaths:
|
||||
STORAGE_HOSTS_PATH = pathlib.Path("/var/opt/instant-workstation/storage-hosts")
|
||||
|
||||
def __init__(self, request):
|
||||
self.persistence_type = request['persistence_type']
|
||||
self.storage_host = request['storage_host']
|
||||
self.workstation = request['workstation']
|
||||
self.user_id = request['user_id']
|
||||
|
||||
@property
|
||||
def nvram_filename(self):
|
||||
return f"{self.workstation_key}_NVRAM.img"
|
||||
|
||||
@property
|
||||
def nvram_path(self):
|
||||
return self.storage_host_path / 'firmware' / self.nvram_filename
|
||||
|
||||
@property
|
||||
def nvram_target_filename(self):
|
||||
return f"{self.user_id}_{self.nvram_filename}"
|
||||
|
||||
@property
|
||||
def nvram_target_path(self):
|
||||
return self.storage_host_path / self.persistence_type / self.nvram_target_filename
|
||||
|
||||
@property
|
||||
def staging_path(self):
|
||||
return self.storage_host_path / 'staging'
|
||||
|
||||
@property
|
||||
def storage_host_path(self):
|
||||
return self.STORAGE_HOSTS_PATH / self.storage_host
|
||||
|
||||
@property
|
||||
def target_filename(self):
|
||||
return f"{self.user_id}_{self.template_filename}"
|
||||
|
||||
@property
|
||||
def target_path(self):
|
||||
return self.storage_host_path / self.persistence_type / self.target_filename
|
||||
|
||||
@property
|
||||
def template_filename(self):
|
||||
return f"{self.workstation_key}.qcow2"
|
||||
|
||||
@property
|
||||
def template_path(self):
|
||||
return self.storage_host_path / 'template' / self.template_filename
|
||||
|
||||
@property
|
||||
def workstation_key(self):
|
||||
return (
|
||||
f"{self.workstation['name']}_"
|
||||
f"{self.workstation['version']}_"
|
||||
f"{self.workstation['cpu']}_"
|
||||
f"{self.workstation['bitness']}_"
|
||||
f"{self.workstation['endianness']}"
|
||||
)
|
||||
30
backend/disks/disk_utils.py
Normal file
30
backend/disks/disk_utils.py
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import pathlib
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
def create_os_disk(paths):
|
||||
with tempfile.TemporaryDirectory(dir=paths.staging_path) as temporary_directory:
|
||||
temporary_path = pathlib.Path(temporary_directory) / 'disk.qcow2'
|
||||
|
||||
subprocess.run(
|
||||
args=[
|
||||
'qemu-img', 'create',
|
||||
'--format', 'qcow2',
|
||||
'--backing', paths.template_path,
|
||||
'--backing-format', 'qcow2',
|
||||
temporary_path
|
||||
],
|
||||
check=True,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
temporary_path.replace(paths.target_path)
|
||||
|
||||
def create_nvram_disk(paths):
|
||||
with tempfile.TemporaryDirectory(dir=paths.staging_path) as temporary_directory:
|
||||
temporary_path = pathlib.Path(temporary_directory) / 'NVRAM.img'
|
||||
|
||||
shutil.copyfile(paths.nvram_path, temporary_path)
|
||||
|
||||
temporary_path.replace(paths.nvram_target_path)
|
||||
19
backend/disks/disks.py
Normal file
19
backend/disks/disks.py
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import http.server
|
||||
import signal
|
||||
|
||||
import request_handler
|
||||
|
||||
def shutdown(_signum, _frame):
|
||||
raise SystemExit(0)
|
||||
|
||||
SERVER_ADDRESS = ('', 50005)
|
||||
SERVER = http.server.ThreadingHTTPServer(SERVER_ADDRESS, request_handler.RequestHandler)
|
||||
SERVER.daemon_threads = False
|
||||
|
||||
signal.signal(signal.SIGINT, shutdown)
|
||||
signal.signal(signal.SIGTERM, shutdown)
|
||||
|
||||
try:
|
||||
SERVER.serve_forever()
|
||||
finally:
|
||||
SERVER.server_close()
|
||||
41
backend/disks/request_handler.py
Normal file
41
backend/disks/request_handler.py
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
import http.server
|
||||
import json
|
||||
|
||||
import disk
|
||||
|
||||
class RequestHandler(http.server.BaseHTTPRequestHandler):
|
||||
protocol_version = 'HTTP/1.1'
|
||||
timeout = 10
|
||||
|
||||
def do_DELETE(self):
|
||||
try:
|
||||
disk.delete_disk(self.body())
|
||||
|
||||
except Exception as error:
|
||||
self.log_error("Disk deletion failed: %s", error)
|
||||
self.send_error(500)
|
||||
return
|
||||
|
||||
self.send_success()
|
||||
|
||||
def do_POST(self):
|
||||
try:
|
||||
disk.create_disk(self.body())
|
||||
|
||||
except Exception as error:
|
||||
self.log_error("Disk creation failed: %s", error)
|
||||
self.send_error(500)
|
||||
return
|
||||
|
||||
self.send_success()
|
||||
|
||||
def body(self):
|
||||
content_length = int(self.headers.get('Content-Length', 0))
|
||||
return json.loads(self.rfile.read(content_length))
|
||||
|
||||
def send_success(self):
|
||||
self.close_connection = True
|
||||
self.send_response(200)
|
||||
self.send_header('Content-Length', '0')
|
||||
self.send_header('Connection', 'close')
|
||||
self.end_headers()
|
||||
Loading…
Add table
Add a link
Reference in a new issue