basic docker machine support

This commit is contained in:
dave 2017-02-24 18:11:36 -08:00
parent 1798cf7fd6
commit 2b6b7db0c7
6 changed files with 135 additions and 6 deletions

7
example/docker-disk.json Normal file
View File

@ -0,0 +1,7 @@
{
"disk_id": "dockerdata",
"properties": {
"type": "dockerdisk",
"datastore": "default"
}
}

9
example/docker.json Normal file
View File

@ -0,0 +1,9 @@
{
"machine_id": "dockertest",
"properties": {
"ports": [
[1234, 80]
],
"image": "dpedu/nginx"
}
}

View File

@ -0,0 +1,106 @@
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?

View File

@ -12,6 +12,7 @@ from concurrent.futures import ThreadPoolExecutor
from zhypervisor.logging import setup_logging from zhypervisor.logging import setup_logging
from zhypervisor.machine import MachineSpec from zhypervisor.machine import MachineSpec
from zhypervisor.clients.qmachine import QDisk, IsoDisk from zhypervisor.clients.qmachine import QDisk, IsoDisk
from zhypervisor.clients.dockermachine import DockerDisk
from zhypervisor.util import ZDisk from zhypervisor.util import ZDisk
from zhypervisor.api.api import ZApi from zhypervisor.api.api import ZApi
@ -113,6 +114,8 @@ class ZHypervisorDaemon(object):
disk = QDisk(datastore, disk_id, disk_spec) disk = QDisk(datastore, disk_id, disk_spec)
elif disk_type == "iso": elif disk_type == "iso":
disk = IsoDisk(datastore, disk_id, disk_spec) disk = IsoDisk(datastore, disk_id, disk_spec)
elif disk_type == "dockerdisk":
disk = DockerDisk(datastore, disk_id, disk_spec)
else: else:
raise Exception("Unknown disk type: {}".format(disk_type)) raise Exception("Unknown disk type: {}".format(disk_type))
disk = ZDisk(datastore, disk_id, disk_spec) disk = ZDisk(datastore, disk_id, disk_spec)

View File

@ -1,6 +1,9 @@
import logging import logging
from zhypervisor.clients.qmachine import QMachine from zhypervisor.clients.qmachine import QMachine
from zhypervisor.clients.dockermachine import DockerMachine
MACHINETYPES = {"q": QMachine, "docker": DockerMachine}
class MachineSpec(object): class MachineSpec(object):
@ -18,11 +21,12 @@ class MachineSpec(object):
self.properties = spec self.properties = spec
# TODO replace if/else with better system try:
if self.properties["type"] == "q": machine_type = MACHINETYPES[self.properties.get("type", None)]
self.machine = QMachine(self) except KeyError:
else: raise Exception("Unknown or missing machine type: {}".format(self.properties.get("type", None)))
raise Exception("Unknown machine type: {}".format(self.properties["type"]))
self.machine = machine_type(self)
def start(self): def start(self):
""" """

View File

@ -35,7 +35,7 @@ class Machine(object):
def __init__(self, machine_spec): def __init__(self, machine_spec):
self.spec = machine_spec self.spec = machine_spec
def run_machine(self): def start_machine(self):
""" """
Run the machine and block until it exits (or was killed) Run the machine and block until it exits (or was killed)
""" """