
- Create root.py, kimchi.conf and config.py.in in plugin - Adjust tabs-ext.xml - Move kimchi specific config/API from wok to plugin - Move mockmodel stuff to plugin - Create mechanism to load plugin-specific config through get_custom_conf() method in plugin root and use it for novnc/spice config moved to new classes KimchiPaths and KimchiConfig - Pass all server options in plugins initalization - Adapt sample plugin to receive server options - Drop server-specific functions from kimchi plugin - Move kimchi-specific URI functions to plugin - Move capabilities to kimchi plugin Signed-off-by: Lucio Correia <luciojhc@linux.vnet.ibm.com> Signed-off-by: Gustavo Y. Ribeiro <gyr@linux.vnet.ibm.com> --- plugins/kimchi/__init__.py | 21 +++ plugins/kimchi/config.py.in | 139 ++++++++++++++++++++ plugins/kimchi/kimchi.conf | 45 +++++++ plugins/kimchi/root.py | 70 ++++++++++ plugins/kimchi/ui/config/tab-ext.xml | 14 +- plugins/kimchi/ui/js/src/kimchi.api.js | 55 -------- plugins/kimchi/ui/js/src/kimchi.guest_edit_main.js | 6 +- plugins/kimchi/ui/js/src/kimchi.host.js | 12 +- plugins/kimchi/ui/js/src/kimchi.main.js | 26 ++++ .../ui/js/src/kimchi.repository_edit_main.js | 4 +- .../kimchi/ui/js/src/kimchi.template_add_main.js | 4 +- .../kimchi/ui/js/src/kimchi.template_edit_main.js | 4 +- plugins/kimchi/utils.py | 40 ++++++ plugins/sample/__init__.py | 2 +- src/wok/config.py.in | 122 ----------------- src/wok/server.py | 25 ++-- src/wok/utils.py | 16 --- ui/js/src/wok.main.js | 10 -- 18 files changed, 374 insertions(+), 241 deletions(-) create mode 100644 plugins/kimchi/__init__.py create mode 100644 plugins/kimchi/config.py.in create mode 100644 plugins/kimchi/kimchi.conf create mode 100644 plugins/kimchi/root.py create mode 100644 plugins/kimchi/ui/js/src/kimchi.main.js create mode 100644 plugins/kimchi/utils.py diff --git a/plugins/kimchi/__init__.py b/plugins/kimchi/__init__.py new file mode 100644 index 0000000..9330044 --- /dev/null +++ b/plugins/kimchi/__init__.py @@ -0,0 +1,21 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013-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 root import KimchiRoot +__all__ = [KimchiRoot] diff --git a/plugins/kimchi/config.py.in b/plugins/kimchi/config.py.in new file mode 100644 index 0000000..80b72bd --- /dev/null +++ b/plugins/kimchi/config.py.in @@ -0,0 +1,139 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013-2015 +# +# 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 libvirt +import os +import platform +import threading + +from wok.config import PluginPaths +from wok.xmlutils.utils import xpath_get_text + +kimchiLock = threading.Lock() + +__with_spice__ = "@withspice@" + +# Storage pool constant for read-only pool types +READONLY_POOL_TYPE = ['iscsi', 'scsi', 'mpath'] + + +def get_distros_store(): + return os.path.join(PluginPaths('kimchi').conf_dir, 'distros.d') + + +def get_debugreports_path(): + return os.path.join(PluginPaths('kimchi').state_dir, 'debugreports') + + +def get_screenshot_path(): + return os.path.join(PluginPaths('kimchi').state_dir, 'screenshots') + + +def find_qemu_binary(find_emulator=False): + try: + connect = libvirt.open(None) + except Exception, e: + raise Exception("Unable to get qemu binary location: %s" % e) + try: + xml = connect.getCapabilities() + + # On Little Endian system, the qemu binary is + # qemu-system-ppc64, not qemu-system-ppc64le as expected + arch = platform.machine() + if arch == "ppc64le": + arch = "ppc64" + + if find_emulator: + expr = "/capabilities/guest/arch[@name='%s']\ + /emulator" % arch + else: + expr = "/capabilities/guest/arch[@name='%s']\ + /domain[@type='kvm']/emulator" % arch + res = xpath_get_text(xml, expr) + location = res[0] + except Exception, e: + raise Exception("Unable to get qemu binary location: %s" % e) + finally: + connect.close() + return location + + +class KimchiPaths(PluginPaths): + + def __init__(self): + super(KimchiPaths, self).__init__('kimchi') + self.spice_file = os.path.join(self.ui_dir, + 'spice-html5/pages/spice_auto.html') + + if __with_spice__ == 'yes': + self.spice_dir = self.add_prefix('ui/spice-html5') + elif os.path.exists('@datadir@/spice-html5'): + self.spice_dir = '@datadir@/spice-html5' + else: + self.spice_dir = '/usr/share/spice-html5' + + if os.path.exists('@datadir@/novnc'): + self.novnc_dir = '@datadir@/novnc' + else: + self.novnc_dir = '/usr/share/novnc' + + if self.installed: + self.spice_css_file = os.path.join(self.spice_dir, 'spice.css') + else: + self.spice_css_file = os.path.join(self.spice_dir, 'css/spice.css') + + +kimchiPaths = KimchiPaths() + + +class KimchiConfig(dict): + def __init__(self): + super(KimchiConfig, self).__init__(self) + + custom_config = { + '/novnc': { + 'tools.staticdir.on': True, + 'tools.staticdir.dir': kimchiPaths.novnc_dir, + 'tools.nocache.on': True, + 'tools.wokauth.on': True, + }, + + '/spice_auto.html': { + 'tools.staticfile.on': True, + 'tools.staticfile.filename': kimchiPaths.spice_file, + 'tools.nocache.on': True, + 'tools.wokauth.on': True, + }, + + '/spice-html5': { + 'tools.staticdir.on': True, + 'tools.staticdir.dir': kimchiPaths.spice_dir, + 'tools.nocache.on': True, + }, + + '/spice-html5/spice.css': { + 'tools.staticfile.on': True, + 'tools.staticfile.filename': kimchiPaths.spice_css_file, + 'tools.nocache.on': True, + }, + } + + self.update(custom_config) + diff --git a/plugins/kimchi/kimchi.conf b/plugins/kimchi/kimchi.conf new file mode 100644 index 0000000..1e0ee6b --- /dev/null +++ b/plugins/kimchi/kimchi.conf @@ -0,0 +1,45 @@ +[wok] +enable = True +plugin_class = "KimchiRoot" +uri = "/plugins/kimchi" + +[/] +tools.trailing_slash.on = False +request.methods_with_bodies = ('POST', 'PUT') +tools.nocache.on = True +tools.proxy.on = True +tools.sessions.on = True +tools.sessions.name = 'wok' +tools.sessions.secure = True +tools.sessions.httponly = True +tools.sessions.locking = 'explicit' +tools.sessions.storage_type = 'ram' +tools.sessions.timeout = 10 +tools.wokauth.on = True + +[/data/screenshots] +tools.staticdir.on = True +tools.staticdir.dir = wok.config.PluginPaths('kimchi').state_dir + '/screenshots' +tools.nocache.on = False + +[/data/debugreports] +tools.staticdir.on = True +tools.staticdir.dir = wok.config.PluginPaths('kimchi').state_dir + '/debugreports' +tools.nocache.on = False +tools.wokauth.on = True +tools.staticdir.content_types = {'xz': 'application/x-xz'} + +[/favicon.ico] +tools.staticfile.on = True +tools.staticfile.filename = wok.config.PluginPaths('kimchi').ui_dir + '/images/logo.ico' + +[/robots.txt] +tools.staticfile.on = True +tools.staticfile.filename = wok.config.PluginPaths('kimchi').ui_dir + '/robots.txt' + +[/help] +tools.staticdir.on = True +tools.staticdir.dir = wok.config.PluginPaths('kimchi').ui_dir + '/pages/help' +tools.nocache.on = True +tools.staticdir.index = 'en_US/index.html' + diff --git a/plugins/kimchi/root.py b/plugins/kimchi/root.py new file mode 100644 index 0000000..9889926 --- /dev/null +++ b/plugins/kimchi/root.py @@ -0,0 +1,70 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013-2015 +# +# 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 json +import os +import cherrypy + + +import config +import mockmodel +from control import sub_nodes +from wok.i18n import messages +from model import model as kimchiModel +from wok.root import WokRoot +from wok import vnc + + +class KimchiRoot(WokRoot): + def __init__(self, wok_options): + if hasattr(wok_options, "model"): + self.model = wok_options.model + elif wok_options.test: + self.model = mockmodel.MockModel() + else: + self.model = kimchiModel.Model() + + dev_env = wok_options.environment != 'production' + super(KimchiRoot, self).__init__(self.model, dev_env) + + for ident, node in sub_nodes.items(): + setattr(self, ident, node(self.model)) + + if isinstance(self.model, kimchiModel.Model): + vnc_ws_proxy = vnc.new_ws_proxy() + cherrypy.engine.subscribe('exit', vnc_ws_proxy.terminate) + + self.api_schema = json.load(open(os.path.join(os.path.dirname( + os.path.abspath(__file__)), 'API.json'))) + self.paths = config.kimchiPaths + self.domain = 'kimchi' + self.messages = messages + + make_dirs = [ + os.path.abspath(config.get_distros_store()), + os.path.abspath(config.get_debugreports_path()), + os.path.abspath(config.get_screenshot_path()) + ] + for directory in make_dirs: + if not os.path.isdir(directory): + os.makedirs(directory) + + def get_custom_conf(self): + return config.KimchiConfig() + diff --git a/plugins/kimchi/ui/config/tab-ext.xml b/plugins/kimchi/ui/config/tab-ext.xml index f79684c..ee88c88 100644 --- a/plugins/kimchi/ui/config/tab-ext.xml +++ b/plugins/kimchi/ui/config/tab-ext.xml @@ -1,38 +1,38 @@ <?xml version="1.0" encoding="utf-8"?> -<tabs> +<tabs-ext> <tab> <access role="admin" mode="admin"/> <access role="user" mode="none"/> <title>Host</title> - <path>tabs/host.html</path> + <path>plugins/kimchi/host.html</path> </tab> <tab> <access role="admin" mode="admin"/> <access role="user" mode="byInstance"/> <title>Guests</title> - <path>tabs/guests.html</path> + <path>plugins/kimchi/guests.html</path> </tab> <tab> <access role="admin" mode="admin"/> <access role="user" mode="none"/> <title>Templates</title> - <path>tabs/templates.html</path> + <path>plugins/kimchi/templates.html</path> </tab> <tab> <access role="admin" mode="admin"/> <access role="user" mode="read-only"/> <title>Storage</title> - <path>tabs/storage.html</path> + <path>plugins/kimchi/storage.html</path> </tab> <tab> <access role="admin" mode="admin"/> <access role="user" mode="read-only"/> <title>Network</title> - <path>tabs/network.html</path> + <path>plugins/kimchi/network.html</path> </tab> -</tabs> +</tabs-ext> diff --git a/plugins/kimchi/ui/js/src/kimchi.api.js b/plugins/kimchi/ui/js/src/kimchi.api.js index d4d0c83..0ec3747 100644 --- a/plugins/kimchi/ui/js/src/kimchi.api.js +++ b/plugins/kimchi/ui/js/src/kimchi.api.js @@ -22,29 +22,6 @@ var kimchi = { trackingTasks: [], /** - * A wrapper of jQuery.ajax function to allow custom bindings. - * - * @param settings an extended object to jQuery Ajax settings object - * with some extra properties (see below) - * - * resend: if the XHR has failed due to 401, the XHR can be resent - * after user being authenticated successfully by setting resend - * to true: settings = {resend: true}. It's useful for switching - * pages (Guests, Templates, etc.). - * e.g., the user wants to list guests by clicking Guests tab, - * but he is told not authorized and a login window will pop up. - * After login, the Ajax request for /vms will be resent without - * user clicking the tab again. - * Default to false. - */ - requestJSON : function(settings) { - settings['originalError'] = settings['error']; - settings['error'] = null; - settings['kimchi'] = true; - return $.ajax(settings); - }, - - /** * * Get host capabilities * suc: callback if succeed err: callback if failed @@ -569,25 +546,6 @@ var kimchi = { }); }, - login : function(settings, suc, err) { - $.ajax({ - url : "login", - type : "POST", - contentType : "application/json", - data : JSON.stringify(settings), - dataType : "json" - }).done(suc).fail(err); - }, - - logout : function(suc, err) { - kimchi.requestJSON({ - url : 'logout', - type : 'POST', - contentType : "application/json", - dataType : "json" - }).done(suc).fail(err); - }, - deleteStoragePool : function(poolName, suc, err) { $.ajax({ url : 'plugins/kimchi/storagepools/' + encodeURIComponent(poolName), @@ -611,19 +569,6 @@ var kimchi = { }); }, - listPlugins : function(suc, err, sync) { - kimchi.requestJSON({ - url : 'plugins', - type : 'GET', - contentType : 'application/json', - dataType : 'json', - resend: true, - async : !sync, - success : suc, - error : err - }); - }, - listNetworks : function(suc, err) { wok.requestJSON({ url : 'plugins/kimchi/networks', diff --git a/plugins/kimchi/ui/js/src/kimchi.guest_edit_main.js b/plugins/kimchi/ui/js/src/kimchi.guest_edit_main.js index 25e3703..7105c88 100644 --- a/plugins/kimchi/ui/js/src/kimchi.guest_edit_main.js +++ b/plugins/kimchi/ui/js/src/kimchi.guest_edit_main.js @@ -314,7 +314,7 @@ kimchi.guest_edit_main = function() { }; //set up for PAM var userNodes = {}, groupNodes = {}; - authType = wok.capabilities['auth'] + authType = kimchi.capabilities['auth'] if (authType == 'pam') { $("#form-guest-edit-permission .ldap").hide(); kimchi.retrieveVM(kimchi.selectedGuest, function(vm){ @@ -432,7 +432,7 @@ kimchi.guest_edit_main = function() { var setupPCIDevice = function(){ kimchi.getHostPCIDevices(function(hostPCIs){ kimchi.getVMPCIDevices(kimchi.selectedGuest, function(vmPCIs){ - var pciEnabled = wok.capabilities.kernel_vfio; + var pciEnabled = kimchi.capabilities.kernel_vfio; for(var i=0; i<hostPCIs.length; i++){ var itemNode = $.parseHTML(wok.substitute($('#pci-tmpl').html(),{ name: hostPCIs[i].name, @@ -708,7 +708,7 @@ kimchi.guest_edit_main = function() { var permissionSubmit = function(event) { var content = { users: [], groups: [] }; - authType = wok.capabilities['auth'] + authType = kimchi.capabilities['auth'] if (authType == 'pam') { $("#permission-sel-users").children().each(function(){ content.users.push($("label", this).text()); diff --git a/plugins/kimchi/ui/js/src/kimchi.host.js b/plugins/kimchi/ui/js/src/kimchi.host.js index 4218d97..ab02333 100644 --- a/plugins/kimchi/ui/js/src/kimchi.host.js +++ b/plugins/kimchi/ui/js/src/kimchi.host.js @@ -507,14 +507,14 @@ kimchi.host_main = function() { }); var setupUI = function() { - if (wok.capabilities == undefined) { + if (kimchi.capabilities == undefined) { setTimeout(setupUI, 2000); return; } - if((wok.capabilities['repo_mngt_tool']) && (wok.capabilities['repo_mngt_tool']!="None")) { - initRepositoriesGrid(wok.capabilities['repo_mngt_tool']); - $('#repositories-section').switchClass('hidden', wok.capabilities['repo_mngt_tool']); + if((kimchi.capabilities['repo_mngt_tool']) && (kimchi.capabilities['repo_mngt_tool']!="None")) { + initRepositoriesGrid(kimchi.capabilities['repo_mngt_tool']); + $('#repositories-section').switchClass('hidden', kimchi.capabilities['repo_mngt_tool']); wok.topic('kimchi/repositoryAdded') .subscribe(listRepositories); wok.topic('kimchi/repositoryUpdated') @@ -523,7 +523,7 @@ kimchi.host_main = function() { .subscribe(listRepositories); } - if(wok.capabilities['update_tool']) { + if(kimchi.capabilities['update_tool']) { $('#software-update-section').removeClass('hidden'); initSoftwareUpdatesGrid(); wok.topic('kimchi/softwareUpdated') @@ -533,7 +533,7 @@ kimchi.host_main = function() { }); } - if(wok.capabilities['system_report_tool']) { + if(kimchi.capabilities['system_report_tool']) { listDebugReports(); wok.topic('kimchi/debugReportAdded') .subscribe(listDebugReports); diff --git a/plugins/kimchi/ui/js/src/kimchi.main.js b/plugins/kimchi/ui/js/src/kimchi.main.js new file mode 100644 index 0000000..2fdeb85 --- /dev/null +++ b/plugins/kimchi/ui/js/src/kimchi.main.js @@ -0,0 +1,26 @@ +/* + * Project Kimchi + * + * Copyright IBM, Corp. 2013-2014 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +kimchi.capabilities = undefined; +kimchi.getCapabilities(function(result) { + kimchi.capabilities = result; + + if(kimchi.capabilities.federation=="on") + $('#peers').removeClass('hide-content'); +}, function() { + kimchi.capabilities = {}; +}); diff --git a/plugins/kimchi/ui/js/src/kimchi.repository_edit_main.js b/plugins/kimchi/ui/js/src/kimchi.repository_edit_main.js index 85e24d4..5bfc51e 100644 --- a/plugins/kimchi/ui/js/src/kimchi.repository_edit_main.js +++ b/plugins/kimchi/ui/js/src/kimchi.repository_edit_main.js @@ -21,10 +21,10 @@ kimchi.repository_edit_main = function() { var saveButton = $('#repository-edit-button-save'); - if(wok.capabilities['repo_mngt_tool']=="yum") { + if(kimchi.capabilities['repo_mngt_tool']=="yum") { editForm.find('input.deb').prop('disabled', true); } - else if(wok.capabilities['repo_mngt_tool']=="deb") { + else if(kimchi.capabilities['repo_mngt_tool']=="deb") { editForm.find('input.yum').prop('disabled', true); } diff --git a/plugins/kimchi/ui/js/src/kimchi.template_add_main.js b/plugins/kimchi/ui/js/src/kimchi.template_add_main.js index 5528eb2..01a47c2 100644 --- a/plugins/kimchi/ui/js/src/kimchi.template_add_main.js +++ b/plugins/kimchi/ui/js/src/kimchi.template_add_main.js @@ -224,12 +224,12 @@ kimchi.template_add_main = function() { $('#iso-remote').css('opacity', 0.3).css('cursor', 'not-allowed'); var enabledRemoteIso = function() { - if (wok.capabilities == undefined) { + if (kimchi.capabilities == undefined) { setTimeout(enabledRemoteIso, 2000); return; } - if (wok.capabilities.qemu_stream != true) { + if (kimchi.capabilities.qemu_stream != true) { return; } diff --git a/plugins/kimchi/ui/js/src/kimchi.template_edit_main.js b/plugins/kimchi/ui/js/src/kimchi.template_edit_main.js index 6df1328..b00d596 100644 --- a/plugins/kimchi/ui/js/src/kimchi.template_edit_main.js +++ b/plugins/kimchi/ui/js/src/kimchi.template_edit_main.js @@ -49,11 +49,11 @@ kimchi.template_edit_main = function() { $('#template-edit-graphics').append('<option>Spice</option>'); kimchi.select('template-edit-graphics-list', vncOpt); var enableSpice = function() { - if (wok.capabilities == undefined) { + if (kimchi.capabilities == undefined) { setTimeout(enableSpice, 2000); return; } - if (wok.capabilities.qemu_spice == true) { + if (kimchi.capabilities.qemu_spice == true) { spiceOpt = [{label: 'Spice', value: 'spice'}] kimchi.select('template-edit-graphics-list', spiceOpt); } diff --git a/plugins/kimchi/utils.py b/plugins/kimchi/utils.py new file mode 100644 index 0000000..dc00481 --- /dev/null +++ b/plugins/kimchi/utils.py @@ -0,0 +1,40 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013-2015 +# +# 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 + +from wok.exception import InvalidParameter + + +def _uri_to_name(collection, uri): + expr = '/plugins/kimchi/%s/(.*?)$' % collection + m = re.match(expr, uri) + if not m: + raise InvalidParameter("KCHUTILS0001E", {'uri': uri}) + return m.group(1) + + +def template_name_from_uri(uri): + return _uri_to_name('templates', uri) + + +def pool_name_from_uri(uri): + return _uri_to_name('storagepools', uri) + diff --git a/plugins/sample/__init__.py b/plugins/sample/__init__.py index 3a309a5..a2cfbf2 100644 --- a/plugins/sample/__init__.py +++ b/plugins/sample/__init__.py @@ -35,7 +35,7 @@ model = Model() class Drawings(WokRoot): - def __init__(self): + def __init__(self, wok_options): Resource.__init__(self, model) self.description = Description(model) self.rectangles = Rectangles(model) diff --git a/src/wok/config.py.in b/src/wok/config.py.in index 390ba3b..c676f89 100644 --- a/src/wok/config.py.in +++ b/src/wok/config.py.in @@ -18,98 +18,32 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # -import libvirt import os -import platform -import threading from ConfigParser import SafeConfigParser -from kimchi.xmlutils.utils import xpath_get_text - __version__ = "@wokversion@" __release__ = "@wokrelease@" -__with_spice__ = "@withspice@" DEFAULT_LOG_LEVEL = "debug" -kimchiLock = threading.Lock() - -# Storage pool constant for read-only pool types -READONLY_POOL_TYPE = ['iscsi', 'scsi', 'mpath'] - def get_object_store(): return os.path.join(paths.state_dir, 'objectstore') -def get_distros_store(): - return os.path.join(paths.conf_dir, 'distros.d') - - -def get_screenshot_path(): - return os.path.join(paths.state_dir, 'screenshots') - - -def get_debugreports_path(): - return os.path.join(paths.state_dir, 'debugreports') - - def get_version(): return "-".join([__version__, __release__]) -def find_qemu_binary(find_emulator=False): - try: - connect = libvirt.open(None) - except Exception, e: - raise Exception("Unable to get qemu binary location: %s" % e) - try: - xml = connect.getCapabilities() - - # On Little Endian system, the qemu binary is - # qemu-system-ppc64, not qemu-system-ppc64le as expected - arch = platform.machine() - if arch == "ppc64le": - arch = "ppc64" - - if find_emulator: - expr = "/capabilities/guest/arch[@name='%s']\ - /emulator" % arch - else: - expr = "/capabilities/guest/arch[@name='%s']\ - /domain[@type='kvm']/emulator" % arch - res = xpath_get_text(xml, expr) - location = res[0] - except Exception, e: - raise Exception("Unable to get qemu binary location: %s" % e) - finally: - connect.close() - return location - - class Paths(object): def __init__(self): self.prefix = self.get_prefix() self.installed = (self.prefix == '@pkgdatadir@') self.ui_dir = self.add_prefix('ui') - self.spice_file = os.path.join(self.ui_dir, - 'spice-html5/pages/spice_auto.html') - - if __with_spice__ == 'yes': - self.spice_dir = self.add_prefix('ui/spice-html5') - elif os.path.exists('@datadir@/spice-html5'): - self.spice_dir = '@datadir@/spice-html5' - else: - self.spice_dir = '/usr/share/spice-html5' - - if os.path.exists('@datadir@/novnc'): - self.novnc_dir = '@datadir@/novnc' - else: - self.novnc_dir = '/usr/share/novnc' if self.installed: self.nginx_conf_dir = '@sysconfdir@/nginx/conf.d' @@ -119,7 +53,6 @@ class Paths(object): self.src_dir = '@wokdir@' self.plugins_dir = '@wokdir@/plugins' self.mo_dir = '@prefix@/share/locale' - self.spice_css_file = os.path.join(self.spice_dir, 'spice.css') else: self.nginx_conf_dir = self.add_prefix('src/nginx') self.state_dir = self.add_prefix('data') @@ -128,7 +61,6 @@ class Paths(object): self.src_dir = self.add_prefix('src/wok') self.plugins_dir = self.add_prefix('plugins') self.mo_dir = self.add_prefix('mo') - self.spice_css_file = os.path.join(self.spice_dir, 'css/spice.css') def get_prefix(self): if __file__.startswith("/"): @@ -208,63 +140,9 @@ class WokConfig(dict): 'tools.sessions.timeout': SESSIONSTIMEOUT, 'tools.wokauth.on': False }, - '/novnc': { - 'tools.staticdir.on': True, - 'tools.staticdir.dir': paths.novnc_dir, - 'tools.nocache.on': True, - 'tools.wokauth.on': True - }, - '/spice_auto.html': { - 'tools.staticfile.on': True, - 'tools.staticfile.filename': paths.spice_file, - 'tools.nocache.on': True, - 'tools.kimchiauth.on': True - }, - '/spice-html5': { - 'tools.staticdir.on': True, - 'tools.staticdir.dir': paths.spice_dir, - 'tools.nocache.on': True - }, - '/spice-html5/spice.css': { - 'tools.staticfile.on': True, - 'tools.staticfile.filename': paths.spice_css_file, - 'tools.nocache.on': True, - }, '/wok-ui.html': { 'tools.wokauth.on': True }, - '/data/screenshots': { - 'tools.staticdir.on': True, - 'tools.staticdir.dir': get_screenshot_path(), - 'tools.nocache.on': False - }, - '/data/debugreports': { - 'tools.staticdir.on': True, - 'tools.staticdir.dir': get_debugreports_path(), - 'tools.nocache.on': False, - 'tools.kimchiauth.on': True, - 'tools.staticdir.content_types': {'xz': 'application/x-xz'} - }, - '/config/ui/tabs.xml': { - 'tools.staticfile.on': True, - 'tools.staticfile.filename': '%s/config/ui/tabs.xml' % - paths.prefix, - 'tools.nocache.on': True - }, - '/favicon.ico': { - 'tools.staticfile.on': True, - 'tools.staticfile.filename': '%s/images/logo.ico' % paths.ui_dir - }, - '/robots.txt': { - 'tools.staticfile.on': True, - 'tools.staticfile.filename': '%s/robots.txt' % paths.ui_dir - }, - '/help': { - 'tools.staticdir.on': True, - 'tools.staticdir.dir': '%s/ui/pages/help' % paths.prefix, - 'tools.staticdir.index': 'en_US/index.html', - 'tools.nocache.on': True - } } def __init__(self): diff --git a/src/wok/server.py b/src/wok/server.py index ab941e7..ba81308 100644 --- a/src/wok/server.py +++ b/src/wok/server.py @@ -26,8 +26,6 @@ import os from wok import auth from wok import config from wok.model import model -from wok import mockmodel -from wok import vnc from wok.config import WokConfig, PluginConfig from wok.control import sub_nodes from wok.proxy import start_proxy, terminate_proxy @@ -64,10 +62,7 @@ class Server(object): make_dirs = [ os.path.dirname(os.path.abspath(options.access_log)), os.path.dirname(os.path.abspath(options.error_log)), - os.path.dirname(os.path.abspath(config.get_object_store())), - os.path.abspath(config.get_screenshot_path()), - os.path.abspath(config.get_debugreports_path()), - os.path.abspath(config.get_distros_store()) + os.path.dirname(os.path.abspath(config.get_object_store())) ] for directory in make_dirs: if not os.path.isdir(directory): @@ -126,15 +121,9 @@ class Server(object): if hasattr(options, 'model'): model_instance = options.model - elif options.test: - model_instance = mockmodel.MockModel() else: model_instance = model.Model() - if isinstance(model_instance, model.Model): - vnc_ws_proxy = vnc.new_ws_proxy() - cherrypy.engine.subscribe('exit', vnc_ws_proxy.terminate) - for ident, node in sub_nodes.items(): if node.url_auth: cfg = self.configObj @@ -143,14 +132,14 @@ class Server(object): self.app = cherrypy.tree.mount(WokRoot(model_instance, dev_env), config=self.configObj) - self._load_plugins() + self._load_plugins(options) # Terminate proxy when cherrypy server is terminated cherrypy.engine.subscribe('exit', terminate_proxy) cherrypy.lib.sessions.init() - def _load_plugins(self): + def _load_plugins(self, options): for plugin_name, plugin_config in get_enabled_plugins(): try: plugin_class = ('plugins.%s.%s' % @@ -164,11 +153,17 @@ class Server(object): continue try: - plugin_app = import_class(plugin_class)() + plugin_app = import_class(plugin_class)(options) except ImportError: cherrypy.log.error_log.error("Failed to import plugin %s" % plugin_class) continue + + # dynamically extend plugin config with custom data, if provided + get_custom_conf = getattr(plugin_app, "get_custom_conf", None) + if get_custom_conf is not None: + plugin_config.update(get_custom_conf()) + cherrypy.tree.mount(plugin_app, script_name, plugin_config) def start(self): diff --git a/src/wok/utils.py b/src/wok/utils.py index fc76620..af0d200 100644 --- a/src/wok/utils.py +++ b/src/wok/utils.py @@ -44,22 +44,6 @@ wok_log = cherrypy.log.error_log task_id = 0 -def _uri_to_name(collection, uri): - expr = '/%s/(.*?)$' % collection - m = re.match(expr, uri) - if not m: - raise InvalidParameter("KCHUTILS0001E", {'uri': uri}) - return m.group(1) - - -def template_name_from_uri(uri): - return _uri_to_name('templates', uri) - - -def pool_name_from_uri(uri): - return _uri_to_name('storagepools', uri) - - def get_next_task_id(): global task_id task_id += 1 diff --git a/ui/js/src/wok.main.js b/ui/js/src/wok.main.js index 5b1f1bc..b7797dd 100644 --- a/ui/js/src/wok.main.js +++ b/ui/js/src/wok.main.js @@ -17,16 +17,6 @@ */ wok.tabMode = {}; -wok.capabilities = undefined; -wok.getCapabilities(function(result) { - wok.capabilities = result; - - if(wok.capabilities.federation=="on") - $('#peers').removeClass('hide-content'); -}, function() { - wok.capabilities = {}; -}); - wok.main = function() { wok.isLoggingOut = false; wok.popable(); -- 1.7.1