107 lines
3.4 KiB
Python
107 lines
3.4 KiB
Python
|
import os
|
||
|
import logging
|
||
|
import subprocess
|
||
|
from time import sleep
|
||
|
from threading import Thread
|
||
|
from zhypervisor.util import ZDisk
|
||
|
from zhypervisor.util import Machine
|
||
|
|
||
|
|
||
|
class DockerMachine(Machine):
|
||
|
machine_type = "docker"
|
||
|
|
||
|
def __init__(self, spec):
|
||
|
Machine.__init__(self, spec)
|
||
|
self.proc = None
|
||
|
self.block_respawns = False
|
||
|
|
||
|
def get_status(self):
|
||
|
"""
|
||
|
Return string "stopped" or "running" depending on machine status
|
||
|
"""
|
||
|
return "stopped" if self.proc is None else "running"
|
||
|
|
||
|
def start_machine(self):
|
||
|
"""
|
||
|
If needed, launch the machine.
|
||
|
"""
|
||
|
if self.proc:
|
||
|
raise Exception("Machine already running!")
|
||
|
else:
|
||
|
docker_args = self.get_args()
|
||
|
logging.info("spawning docker with: {}".format(' '.join(docker_args)))
|
||
|
sleep(1) # anti-spin
|
||
|
self.proc = subprocess.Popen(docker_args, preexec_fn=lambda: os.setpgrp())
|
||
|
# TODO handle stdout/err - stream to logs?
|
||
|
Thread(target=self.wait_on_exit, args=[self.proc]).start()
|
||
|
|
||
|
def wait_on_exit(self, proc):
|
||
|
"""
|
||
|
Listener used by above start_machine to restart the machine if the machine exits
|
||
|
"""
|
||
|
proc.wait()
|
||
|
logging.info("docker process has exited")
|
||
|
self.proc = None
|
||
|
if not self.block_respawns and self.spec.properties.get("respawn", False):
|
||
|
self.start_machine()
|
||
|
|
||
|
def stop_machine(self):
|
||
|
"""
|
||
|
Send the powerdown signal to the running machine
|
||
|
"""
|
||
|
if self.proc:
|
||
|
logging.info("stopping machine %s", self.spec.machine_id)
|
||
|
subprocess.check_call(["docker", "stop", self.spec.machine_id])
|
||
|
self.proc.wait()
|
||
|
self.proc = None
|
||
|
|
||
|
def kill_machine(self):
|
||
|
"""
|
||
|
Forcefully kill the running machine
|
||
|
"""
|
||
|
print("Terminating {}".format(self.proc))
|
||
|
if self.proc:
|
||
|
subprocess.check_call(["docker", "kill", self.spec.machine_id])
|
||
|
try:
|
||
|
self.proc.wait(5)
|
||
|
except subprocess.TimeoutError:
|
||
|
self.proc.kill()
|
||
|
self.proc.wait()
|
||
|
self.proc = None
|
||
|
|
||
|
def get_args(self):
|
||
|
"""
|
||
|
Assemble the full argv array that will be executed for this machine
|
||
|
"""
|
||
|
argv = ['docker', 'run', '--rm', '--name', self.spec.machine_id,
|
||
|
'--hostname', self.spec.properties.get("hostname", self.spec.machine_id)]
|
||
|
|
||
|
for hostport, containerport in self.spec.properties.get("ports", []):
|
||
|
argv.append("-p")
|
||
|
argv.append("{}:{}".format(int(hostport), int(containerport)))
|
||
|
|
||
|
for volume in self.spec.properties.get("volumes", []):
|
||
|
disk_ob = self.spec.master.disks[volume["disk"]]
|
||
|
|
||
|
volpath = disk_ob.get_path()
|
||
|
argv.append("-v")
|
||
|
argv.append("{}:{}".format(volpath, volume.get("mountpoint")))
|
||
|
|
||
|
if self.spec.properties.get("stopsignal", False):
|
||
|
argv += ['--stop-signal', int(self.spec.properties.get("stopsignal"))]
|
||
|
|
||
|
argv += ['--stop-timeout', int(self.spec.properties.get("timeout", 25))]
|
||
|
|
||
|
argv.append("{}".format(self.spec.properties.get("image")))
|
||
|
if self.spec.properties.get("cmd", False):
|
||
|
argv.append("{}".format(self.spec.properties.get("cmd")))
|
||
|
|
||
|
return [str(arg) for arg in argv]
|
||
|
|
||
|
|
||
|
class DockerDisk(ZDisk):
|
||
|
|
||
|
def validate(self):
|
||
|
pass
|
||
|
# alphanumeric only? underscores?
|