[PATCH 0/6 V3] Discover Kimchi peers using openSLP

V2 -> V3: - Use self.url while registering server on openSLP V1 -> V2: - Update docs/API.md - Expose federation on /config/capabilities Aline Manera (6): Update kimchi.config values according to command line input Delete http_port from /config API as it is not in use anymore Add federation option to Kimchi config file Discover Kimchi peers using openSLP Add documentation on how to enable federation on Kimchi Expose federation on /config/capabilities docs/API.md | 11 ++++++++- docs/README-federation.md | 27 +++++++++++++++++++++ src/kimchi.conf.in | 4 +++ src/kimchi/config.py.in | 1 + src/kimchi/control/peers.py | 29 ++++++++++++++++++++++ src/kimchi/mockmodel.py | 14 ++++++++--- src/kimchi/model/config.py | 6 ++--- src/kimchi/model/peers.py | 59 +++++++++++++++++++++++++++++++++++++++++++++ src/kimchi/server.py | 1 - src/kimchid.in | 36 +++++++++++++++++---------- tests/test_rest.py | 19 +++++++++------ 11 files changed, 177 insertions(+), 30 deletions(-) create mode 100644 docs/README-federation.md create mode 100644 src/kimchi/control/peers.py create mode 100644 src/kimchi/model/peers.py -- 1.9.3

Kimchi allows users to change some configuration values by command line. Those values override the kimchi.conf values. That way we need to properly update kimchi.config.config to provide the accurate data for whole application. Signed-off-by: Aline Manera <alinefm@linux.vnet.ibm.com> --- src/kimchid.in | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/kimchid.in b/src/kimchid.in index d3d398f..fc889a3 100644 --- a/src/kimchid.in +++ b/src/kimchid.in @@ -26,12 +26,11 @@ sys.path.insert(1, '@pythondir@') from optparse import OptionParser import kimchi.server -import kimchi.config +import kimchi.config as config -from kimchi.config import config, paths -if not paths.installed: - sys.path.append(paths.prefix) +if not config.paths.installed: + sys.path.append(config.paths.prefix) ACCESS_LOG = "kimchi-access.log" ERROR_LOG = "kimchi-error.log" @@ -42,13 +41,13 @@ def main(options): if not os.geteuid() == 0: sys.exit("\nMust be root to run this script. Exiting ...\n") - host = config.get("server", "host") - port = config.get("server", "port") - ssl_port = config.get("server", "ssl_port") - cherrypy_port = config.get("server", "cherrypy_port") - runningEnv = config.get('server', 'environment') - logDir = config.get("logging", "log_dir") - logLevel = config.get("logging", "log_level") + host = config.config.get("server", "host") + port = config.config.get("server", "port") + ssl_port = config.config.get("server", "ssl_port") + cherrypy_port = config.config.get("server", "cherrypy_port") + runningEnv = config.config.get("server", "environment") + logDir = config.config.get("logging", "log_dir") + logLevel = config.config.get("logging", "log_level") parser = OptionParser() parser.add_option('--host', type="string", default=host, @@ -73,9 +72,16 @@ def main(options): help="Run server in mock model") (options, args) = parser.parse_args() + # Update config.config with the command line values + # So the whole application will have access to accurate values + for sec in config.config.sections(): + for item in config.config.options(sec): + if hasattr(options, item): + config.config.set(sec, item, str(getattr(options, item))) + # Add non-option arguments - setattr(options, 'ssl_cert', config.get('server', 'ssl_cert')) - setattr(options, 'ssl_key', config.get('server', 'ssl_key')) + setattr(options, 'ssl_cert', config.config.get('server', 'ssl_cert')) + setattr(options, 'ssl_key', config.config.get('server', 'ssl_key')) kimchi.server.main(options) -- 1.9.3

http_port information was used to display the guest console But since we have changed how to redirect user to guest console by adding the ui/pages/websockify/console.html it is not in use anymore. So remove it. Signed-off-by: Aline Manera <alinefm@linux.vnet.ibm.com> --- docs/API.md | 1 - src/kimchi/mockmodel.py | 5 ++--- src/kimchi/model/config.py | 3 +-- src/kimchi/server.py | 1 - tests/test_rest.py | 3 ++- 5 files changed, 5 insertions(+), 8 deletions(-) diff --git a/docs/API.md b/docs/API.md index 20f5f23..13db844 100644 --- a/docs/API.md +++ b/docs/API.md @@ -598,7 +598,6 @@ Contains information about the application environment and configuration. **Methods:** * **GET**: Retrieve configuration information - * http_port: The port number on which the server is listening * display_proxy_port: Port for vnc and spice's websocket proxy to listen on * version: The version of the kimchi service * **POST**: *See Configuration Actions* diff --git a/src/kimchi/mockmodel.py b/src/kimchi/mockmodel.py index e5e5467..5799d3c 100644 --- a/src/kimchi/mockmodel.py +++ b/src/kimchi/mockmodel.py @@ -924,9 +924,8 @@ class MockModel(object): return disks.get_partition_details(name) def config_lookup(self, name): - return {'http_port': cherrypy.config.nginx_port, - 'display_proxy_port': - kconfig.get('display', 'display_proxy_port'), + return {'display_proxy_port': kconfig.get('display', + 'display_proxy_port'), 'version': config.get_version()} def packagesupdate_get_list(self): diff --git a/src/kimchi/model/config.py b/src/kimchi/model/config.py index 0466f6f..90bb923 100644 --- a/src/kimchi/model/config.py +++ b/src/kimchi/model/config.py @@ -40,8 +40,7 @@ class ConfigModel(object): def lookup(self, name): proxy_port = kconfig.get('display', 'display_proxy_port') - return {'http_port': cherrypy.config.nginx_port, - 'display_proxy_port': proxy_port, + return {'display_proxy_port': proxy_port, 'version': get_version()} diff --git a/src/kimchi/server.py b/src/kimchi/server.py index bca2147..10f5dff 100644 --- a/src/kimchi/server.py +++ b/src/kimchi/server.py @@ -87,7 +87,6 @@ class Server(object): # directly. You must go through the proxy. cherrypy.server.socket_host = '127.0.0.1' cherrypy.server.socket_port = options.cherrypy_port - cherrypy.config.nginx_port = options.port cherrypy.log.screen = True cherrypy.log.access_file = options.access_log diff --git a/tests/test_rest.py b/tests/test_rest.py index 0bf5997..ca50f9e 100644 --- a/tests/test_rest.py +++ b/tests/test_rest.py @@ -1526,7 +1526,8 @@ class RestTests(unittest.TestCase): def test_config(self): resp = self.request('/config').read() conf = json.loads(resp) - self.assertEquals(port, conf['http_port']) + keys = ["display_proxy_port", "version"] + self.assertEquals(keys, sorted(conf.keys())) def test_capabilities(self): resp = self.request('/config/capabilities').read() -- 1.9.3

It is also allows user to enable/disable the federation feature by the command line: kimchid --federation=[on|off] The default value is: off which means the federation feature will be disabled Signed-off-by: Aline Manera <alinefm@linux.vnet.ibm.com> --- src/kimchi.conf.in | 4 ++++ src/kimchi/config.py.in | 1 + src/kimchid.in | 4 ++++ 3 files changed, 9 insertions(+) diff --git a/src/kimchi.conf.in b/src/kimchi.conf.in index ed0270d..2be1a0e 100644 --- a/src/kimchi.conf.in +++ b/src/kimchi.conf.in @@ -26,6 +26,10 @@ # Running environment of the server #environment = production +# Federation feature: register Kimchi server on openSLP and discover peers +# at the same network +#federation = off + [logging] # Log directory #log_dir = @localstatedir@/log/kimchi diff --git a/src/kimchi/config.py.in b/src/kimchi/config.py.in index fca32ee..d403827 100644 --- a/src/kimchi/config.py.in +++ b/src/kimchi/config.py.in @@ -254,6 +254,7 @@ def _get_config(): config.set("server", "ssl_cert", "") config.set("server", "ssl_key", "") config.set("server", "environment", "production") + config.set("server", "federation", "off") config.add_section("logging") config.set("logging", "log_dir", paths.log_dir) config.set("logging", "log_level", DEFAULT_LOG_LEVEL) diff --git a/src/kimchid.in b/src/kimchid.in index fc889a3..3ed087f 100644 --- a/src/kimchid.in +++ b/src/kimchid.in @@ -46,6 +46,7 @@ def main(options): ssl_port = config.config.get("server", "ssl_port") cherrypy_port = config.config.get("server", "cherrypy_port") runningEnv = config.config.get("server", "environment") + federation = config.config.get("server", "federation") logDir = config.config.get("logging", "log_dir") logLevel = config.config.get("logging", "log_level") @@ -68,6 +69,9 @@ def main(options): help="Error log file") parser.add_option('--environment', default=runningEnv, help="Running environment of kimchi server") + parser.add_option('--federation', default=federation, + help="Register and discover Kimchi peers at the same " + "network using openSLP") parser.add_option('--test', action='store_true', help="Run server in mock model") (options, args) = parser.parse_args() -- 1.9.3

When federation feature is enabled, Kimchi server will be registered on openSLP service on server starting up. It will expose the Kimchi URL to others Kimchi peers. To discover Kimchi peers in the same network, the following API is provided: GET /peers [ https://ubuntu-vm:8001, https://rhel-vm:8001 ] Signed-off-by: Aline Manera <alinefm@linux.vnet.ibm.com> --- docs/API.md | 9 +++++++ src/kimchi/control/peers.py | 29 ++++++++++++++++++++++ src/kimchi/mockmodel.py | 6 +++++ src/kimchi/model/peers.py | 59 +++++++++++++++++++++++++++++++++++++++++++++ tests/test_rest.py | 4 +++ 5 files changed, 107 insertions(+) create mode 100644 src/kimchi/control/peers.py create mode 100644 src/kimchi/model/peers.py diff --git a/docs/API.md b/docs/API.md index 13db844..4a7e9e1 100644 --- a/docs/API.md +++ b/docs/API.md @@ -1005,3 +1005,12 @@ http://, ftp:// or file:// URL. * enable: Enable the Repository as package source * disable: Disable the Repository as package source + +### Collection: Peers + +**URI:** /peers + +**Methods:** + +* **GET**: Return the list of Kimchi peers in the same network + (It uses openSLP for discovering) diff --git a/src/kimchi/control/peers.py b/src/kimchi/control/peers.py new file mode 100644 index 0000000..f72a38c --- /dev/null +++ b/src/kimchi/control/peers.py @@ -0,0 +1,29 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2014 +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +from kimchi.control.base import SimpleCollection +from kimchi.control.utils import UrlSubNode + + +@UrlSubNode("peers", True) +class Peers(SimpleCollection): + def __init__(self, model): + super(Peers, self).__init__(model) + self.role_key = 'peers' + self.admin_methods = ['GET'] diff --git a/src/kimchi/mockmodel.py b/src/kimchi/mockmodel.py index 5799d3c..6c07a38 100644 --- a/src/kimchi/mockmodel.py +++ b/src/kimchi/mockmodel.py @@ -890,6 +890,12 @@ class MockModel(object): def groups_get_list(self): return ["groupA", "groupB", "groupC", "groupD"] + def peers_get_list(self): + if kconfig.get("server", "federation") == "off": + return [] + + return ["https://serverA:8001", "https://serverB:8001"] + def vms_get_list_by_state(self, state): ret_list = [] for name in self.vms_get_list(): diff --git a/src/kimchi/model/peers.py b/src/kimchi/model/peers.py new file mode 100644 index 0000000..1bf74cb --- /dev/null +++ b/src/kimchi/model/peers.py @@ -0,0 +1,59 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2014 +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +import re +import socket + +from kimchi.config import config +from kimchi.utils import kimchi_log, run_command + + +class PeersModel(object): + def __init__(self, **kargs): + # check federation feature is enabled on Kimchi server + if config.get("server", "federation") == "off": + return + + # register server on openslp + hostname = socket.getfqdn(config.get("server", "host")) + port = config.get("server", "ssl_port") + self.url = hostname + ":" + port + + cmd = ["slptool", "register", + "service:kimchid://%s" % self.url] + out, error, ret = run_command(cmd) + if len(out) != 0: + kimchi_log.error("Unable to register server on openSLP." + " Details: %s" % out) + + def get_list(self): + # check federation feature is enabled on Kimchi server + if config.get("server", "federation") == "off": + return [] + + peers = [] + cmd = ["slptool", "findsrvs", "service:kimchid"] + out, error, ret = run_command(cmd) + for server in out.strip().split("\n"): + match = re.match("service:kimchid://(.*?),.*", server) + peer = match.group(1) + if peer != self.url: + peers.append("https://" + peer) + + return peers diff --git a/tests/test_rest.py b/tests/test_rest.py index ca50f9e..b22a3d8 100644 --- a/tests/test_rest.py +++ b/tests/test_rest.py @@ -1540,6 +1540,10 @@ class RestTests(unittest.TestCase): self.assertIn('update_tool', conf) self.assertIn('repo_mngt_tool', conf) + def test_peers(self): + resp = self.request('/peers').read() + self.assertEquals([], json.loads(resp)) + def test_auth_unprotected(self): hdrs = {'AUTHORIZATION': ''} uris = ['/js/kimchi.min.js', -- 1.9.3

README-federation.md is specific for the federation feature. It explains how to install openSLP and enable federation on Kimchi. Signed-off-by: Aline Manera <alinefm@linux.vnet.ibm.com> --- docs/README-federation.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 docs/README-federation.md diff --git a/docs/README-federation.md b/docs/README-federation.md new file mode 100644 index 0000000..fdf4aed --- /dev/null +++ b/docs/README-federation.md @@ -0,0 +1,27 @@ +Kimchi Project - Federation Feature +=================================== + +Federation feature is a mechanism to discover Kimchi peers in the same network. +It uses openSLP tool (http://www.openslp.org/) to register and find the Kimchi +servers. + +By default this feature is disabled on Kimchi as it is not critical for KVM +virtualization and requires additional software installation. + +To enable it, do the following: + +1) Install openslp and openslp-server packages +2) openSLP uses port 427 (UDP) and port 427 (TCP) so make sure to open those + ports in your firewall configuration +3) Start slpd service and make sure it is up while running Kimchi +4) Enable federation on Kimchi by editing the /etc/kimchi/kimchi.conf file: + + federation = on + +5) Then restart Kimchi service + +The Kimchi server will be registered on openSLP on server starting up and will +be found by other Kimchi peers (with federation feature enabled) in the same +network. + +Enjoy! -- 1.9.3

That way we can properly update the UI when federation is enabled or not Signed-off-by: Aline Manera <alinefm@linux.vnet.ibm.com> --- docs/API.md | 1 + src/kimchi/mockmodel.py | 3 ++- src/kimchi/model/config.py | 3 ++- tests/test_rest.py | 12 +++++------- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/docs/API.md b/docs/API.md index 4a7e9e1..40a0c0b 100644 --- a/docs/API.md +++ b/docs/API.md @@ -629,6 +629,7 @@ creation. system; False, otherwise * repo_mngt_tool: 'deb', 'yum' or None - when the repository management tool is not identified + * federation: 'on' if federation feature is enabled, 'off' otherwise. * **POST**: *See Configuration Actions* **Actions (POST):** diff --git a/src/kimchi/mockmodel.py b/src/kimchi/mockmodel.py index 6c07a38..e23c21a 100644 --- a/src/kimchi/mockmodel.py +++ b/src/kimchi/mockmodel.py @@ -74,7 +74,8 @@ class MockModel(object): 'screenshot': True, 'system_report_tool': True, 'update_tool': True, - 'repo_mngt_tool': 'yum'} + 'repo_mngt_tool': 'yum', + 'federation': 'off'} def reset(self): self._mock_vms = {} diff --git a/src/kimchi/model/config.py b/src/kimchi/model/config.py index 90bb923..1c00cfe 100644 --- a/src/kimchi/model/config.py +++ b/src/kimchi/model/config.py @@ -109,7 +109,8 @@ class CapabilitiesModel(object): 'screenshot': VMScreenshot.get_stream_test_result(), 'system_report_tool': bool(report_tool), 'update_tool': update_tool, - 'repo_mngt_tool': repo_mngt_tool} + 'repo_mngt_tool': repo_mngt_tool, + 'federation': kconfig.get("server", "federation")} class DistrosModel(object): diff --git a/tests/test_rest.py b/tests/test_rest.py index b22a3d8..1bbdc47 100644 --- a/tests/test_rest.py +++ b/tests/test_rest.py @@ -1532,13 +1532,11 @@ class RestTests(unittest.TestCase): def test_capabilities(self): resp = self.request('/config/capabilities').read() conf = json.loads(resp) - self.assertIn('libvirt_stream_protocols', conf) - self.assertIn('qemu_stream', conf) - self.assertIn('qemu_spice', conf) - self.assertIn('screenshot', conf) - self.assertIn('system_report_tool', conf) - self.assertIn('update_tool', conf) - self.assertIn('repo_mngt_tool', conf) + + keys = ['libvirt_stream_protocols', 'qemu_stream', 'qemu_spice', + 'screenshot', 'system_report_tool', 'update_tool', + 'repo_mngt_tool', 'federation'] + self.assertEquals(sorted(keys), sorted(conf.keys())) def test_peers(self): resp = self.request('/peers').read() -- 1.9.3

-- Reviewed-by: Paulo Vital <pvital@linux.vnet.ibm.com> On Mon, 2014-09-01 at 10:48 -0300, Aline Manera wrote:
V2 -> V3: - Use self.url while registering server on openSLP
V1 -> V2: - Update docs/API.md - Expose federation on /config/capabilities
Aline Manera (6): Update kimchi.config values according to command line input Delete http_port from /config API as it is not in use anymore Add federation option to Kimchi config file Discover Kimchi peers using openSLP Add documentation on how to enable federation on Kimchi Expose federation on /config/capabilities
docs/API.md | 11 ++++++++- docs/README-federation.md | 27 +++++++++++++++++++++ src/kimchi.conf.in | 4 +++ src/kimchi/config.py.in | 1 + src/kimchi/control/peers.py | 29 ++++++++++++++++++++++ src/kimchi/mockmodel.py | 14 ++++++++--- src/kimchi/model/config.py | 6 ++--- src/kimchi/model/peers.py | 59 +++++++++++++++++++++++++++++++++++++++++++++ src/kimchi/server.py | 1 - src/kimchid.in | 36 +++++++++++++++++---------- tests/test_rest.py | 19 +++++++++------ 11 files changed, 177 insertions(+), 30 deletions(-) create mode 100644 docs/README-federation.md create mode 100644 src/kimchi/control/peers.py create mode 100644 src/kimchi/model/peers.py
participants (2)
-
Aline Manera
-
Paulo Ricardo Paz Vital