Fabian Deutsch has uploaded a new change for review. Change subject: [DRAFT] Add a docker plugin ......................................................................
[DRAFT] Add a docker plugin This plugin allows Node to act as a "docker-host". This means that docker is run in "host mode", binding itself to a public port. Binding to a public port is unsafe, because everyone can use it. Once this is working well, we should bind only locally and allow access via SSH. This plugin - adds docker-io to the image - persists the correct dirs - adds a page to display some basic informations - requires systemd Change-Id: I1021f347285ff5ea8e343003291e610b02a2f838 Signed-off-by: Fabian Deutsch <[email protected]> --- M ovirt-node.spec.in M src/ovirt/node/plugins.py A src/ovirt/node/setup/docker/__init__.py A src/ovirt/node/setup/docker/docker_page.py M src/ovirt/node/utils/system.py 5 files changed, 302 insertions(+), 0 deletions(-) git pull ssh://gerrit.ovirt.org:29418/ovirt-node refs/changes/70/25370/1 diff --git a/ovirt-node.spec.in b/ovirt-node.spec.in index 970eef3..7b1c605 100644 --- a/ovirt-node.spec.in +++ b/ovirt-node.spec.in @@ -342,6 +342,45 @@ %endif +# +# Docker plugin subpackage +# +%global docker_port 4243 +%package plugin-docker +Summary: Become a docker host %{product_family} +Group: Applications/System + +Requires: docker-io +BuildRequires: systemd-units +Requires: systemd + + +%description plugin-docker +This package adds docker (https://www.docker.io/) to %{product_family}. +Docker will be run in daemon mode and attach itself to the public port %{docker_port}. + +A client can connect to the docker host using: +$ docker -H $DOCKER_ADDR:%{docker_port} info + + +%post plugin-docker +if [ $1 -eq 1 ] ; then + # Initial installation + + # Create persisted dir + service docker stop || : + mv /var/lib/docker /data/ + mkdir /var/lib/docker + echo -e "\n/data/docker /var/lib/docker none bind 0 0" >> /etc/fstab + + # Bind docker to external port + sed -i '/^ExecStart/ s/$/ -H 0.0.0.0:%{docker_port}/' /usr/lib/systemd/system/docker.service + + systemctl daemon-reload >/dev/null 2>&1 || : + systemctl enable docker.service >/dev/null 2>&1 || : +fi + + %prep %setup -q diff --git a/src/ovirt/node/plugins.py b/src/ovirt/node/plugins.py index 282f5b5..94223c5 100644 --- a/src/ovirt/node/plugins.py +++ b/src/ovirt/node/plugins.py @@ -525,6 +525,9 @@ def __str__(self): return "<UIElements %s>" % self._elements + def items(self): + return self._elements.items() + class Group(list, base.Base): def __init__(self, uielements, paths): super(UIElements.Group, self).__init__() diff --git a/src/ovirt/node/setup/docker/__init__.py b/src/ovirt/node/setup/docker/__init__.py new file mode 100644 index 0000000..a748432 --- /dev/null +++ b/src/ovirt/node/setup/docker/__init__.py @@ -0,0 +1,32 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# __init__.py - Copyright (C) 2014 Red Hat, Inc. +# Written by Fabian Deutsch <[email protected]> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. A copy of the GNU General Public License is +# also available at http://www.gnu.org/copyleft/gpl.html. + +""" +Docker Plugin +""" +import docker_page + + +# +# Magic function to register all plugins to be used +# +def createPlugins(application): + docker_page.Plugin(application) diff --git a/src/ovirt/node/setup/docker/docker_page.py b/src/ovirt/node/setup/docker/docker_page.py new file mode 100644 index 0000000..a2e436e --- /dev/null +++ b/src/ovirt/node/setup/docker/docker_page.py @@ -0,0 +1,217 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# docker_page.py - Copyright (C) 2012 Red Hat, Inc. +# Written by Fabian Deutsch <[email protected]> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. A copy of the GNU General Public License is +# also available at http://www.gnu.org/copyleft/gpl.html. +from ovirt.node import base, plugins, ui +from ovirt.node.utils import process, system +from subprocess import CalledProcessError +import urllib +import threading +import time + +""" +Docker Status +""" + + +class Plugin(plugins.NodePlugin): + _model = None + watcher = None + + def __init__(self, app): + super(Plugin, self).__init__(app) + self._model = {} + self.watcher = DockerWatcher(self) + self.watcher.start() + + def has_ui(self): + return True + + def name(self): + return "Docker" + + def rank(self): + return 15 + + def model(self): + d = Docker() + is_alive = d.is_alive() + + model = {"docker.is_alive": "%s" % is_alive, + "docker.status": "", + "docker.info": "", + } + + return model + + def validators(self): + return {} + + def ui_content(self): + is_alive = Docker().is_alive() + + DockerAsyncUpdate(self).start() + + ws = [ui.Header("header[0]", "Docker"), + ui.KeywordLabel("docker.is_alive", "Is Alive: "), + ui.Divider("divider[0]"), + ui.KeywordLabel("docker.status", "Status:"), + ] + + page = ui.Page("page", ws) + page.buttons = [ui.Button("action.images", "Images", + enabled=is_alive), + ui.Button("action.containers", "Containers", + enabled=is_alive)] + self.widgets.add(page) + self.widgets.add(page.buttons) + return page + + def on_change(self, changes): + pass + + def on_merge(self, changes): + actions = {"action.images": ["images"], + "action.containers": ["ps"], + } + for key in changes: + if key in actions: + self._dialog = DockerTxtDialog(actions[key]) + return self._dialog + + +class DockerTxtDialog(ui.InfoDialog): + """A simple class to display results of docker subcomands in a dialog + """ + def __init__(self, subcmd): + cmd, txt = Docker().docker(subcmd, with_cmd=True) + title = "$ " + " ".join(cmd) + super(DockerTxtDialog, self).__init__("dialog.docker.txt", title, txt) + + +class DockerWatcher(threading.Thread): + """A thread which watches docker for status changes. If the status changes it updtes the UI + """ + daemon = True + + def __init__(self, plugin): + self.plugin = plugin + self.app = plugin.application + super(DockerWatcher, self).__init__() + + def run(self): + try: + self._run() + except Exception as e: + self.app.logger.debug("DockerWatcher", exc_info=True) + + def on_state_change(self): + DockerAsyncUpdate(self.plugin).run() + + def _run(self): + d = Docker() + last_state = None + while True: + self.app.logger.debug("Checking docker livelyness") + time.sleep(1) + alive = d.is_alive() + has_changed = last_state != alive + if has_changed: + last_state = alive + self.on_state_change() + + +class DockerAsyncUpdate(threading.Thread): + """A thread which gathers information from long running process calls + In a thread to keep the UI responsive + """ + daemon = True + + def __init__(self, plugin): + self.plugin = plugin + self.app = plugin.application + super(DockerAsyncUpdate, self).__init__() + + def run(self): + try: + self._run() + except Exception as e: + self.app.logger.debug("DockerStatusThread", exc_info=True) + + def _run(self): + d = Docker() + alive = d.is_alive() + self._update_ui("docker.is_alive", "%s" % alive) + self._update_ui("docker.status", d.status()) + #self._update_ui("docker.info", d.info() if alive else "") + for n, w in self.plugin.widgets.items(): + self.app.logger.debug("%s" % n) + if n.startswith("action."): + w.enabled(alive) + + def _update_ui(self, path, txt): + con = self.app.ui.thread_connection() + def update_ui(path=path, txt=txt): + self.plugin.widgets[path].value(txt) + con.call(lambda: update_ui()) + + +class Docker(base.Base): + """A wrapper around several docker related binaries + """ + port = 4243 + + def docker(self, args=[], with_cmd=False): + """Wrap docker binary + """ + assert type(args) is list + cmd = ["docker", "-H", ":%s" % self.port] + args + ret = process.check_output(cmd, stderr=process.PIPE) + if with_cmd: + ret = (cmd, ret) + return ret + + def service(self, cmd): + """Wrap docker service + """ + return system.service("docker", cmd) + + def logs(self): + return system.journal("docker") + + def status(self): + status = "" + try: + status = self.service("status") + except process.CalledProcessError as e: + status = e.output + return status + + def info(self): + return self.docker(["info"]) + + def is_alive(self): + alive = False + try: + urllib.urlopen('http://127.0.0.1:%s/' % self.port) + alive = True + except: + #self.logger.debug("Docker is alive") + pass + return alive diff --git a/src/ovirt/node/utils/system.py b/src/ovirt/node/utils/system.py index 18d66c4..a2771aa 100644 --- a/src/ovirt/node/utils/system.py +++ b/src/ovirt/node/utils/system.py @@ -164,6 +164,17 @@ return r +def journal(unit=None, this_boot=True): + """Convenience function to access the journal + """ + cmd = ["journalctl"] + if unit: + cmd += ["--unit", unit] + if this_boot: + cmd += ["--this-boot"] + return process.pipe(cmd) + + def has_systemd(): """Determine if the system has systemd available. """ -- To view, visit http://gerrit.ovirt.org/25370 To unsubscribe, visit http://gerrit.ovirt.org/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I1021f347285ff5ea8e343003291e610b02a2f838 Gerrit-PatchSet: 1 Gerrit-Project: ovirt-node Gerrit-Branch: master Gerrit-Owner: Fabian Deutsch <[email protected]> _______________________________________________ node-patches mailing list [email protected] http://lists.ovirt.org/mailman/listinfo/node-patches
