[node-patches] Change in ovirt-node[master]: [DRAFT] Add a docker plugin
fabiand at fedoraproject.org
fabiand at fedoraproject.org
Wed Mar 5 09:20:40 UTC 2014
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 <fabiand at fedoraproject.org>
---
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 <fabiand at redhat.com>
+#
+# 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 <fabiand at redhat.com>
+#
+# 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 <fabiand at fedoraproject.org>
More information about the node-patches
mailing list