From be10c5605d5376a8c5c14a3be5b306bb951fd0f2 Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Tue, 11 Sep 2007 18:41:35 -0400 Subject: Check in beginings of Cobbler WebUI packaging (thanks: Al Tobey). More work TBA. --- cobbler/action_sync.py | 2 +- cobbler/webui/CobblerWeb.py | 191 +++++++++++++++++++++++++++++++ cobbler/webui/__init__.py | 0 cobbler/webui/master.py | 240 +++++++++++++++++++++++++++++++++++++++ cobbler/webui/webui-cgi.py | 47 ++++++++ cobbler/webui/webui-cherrypy3.py | 10 ++ 6 files changed, 489 insertions(+), 1 deletion(-) create mode 100644 cobbler/webui/CobblerWeb.py create mode 100644 cobbler/webui/__init__.py create mode 100644 cobbler/webui/master.py create mode 100755 cobbler/webui/webui-cgi.py create mode 100755 cobbler/webui/webui-cherrypy3.py (limited to 'cobbler') diff --git a/cobbler/action_sync.py b/cobbler/action_sync.py index 19c6d63..86aaa2f 100644 --- a/cobbler/action_sync.py +++ b/cobbler/action_sync.py @@ -249,7 +249,7 @@ class BootSync: if not x.endswith(".py"): self.rmfile(path) if os.path.isdir(path): - if not x in ["localmirror","repo_mirror","ks_mirror","kickstarts","kickstarts_sys","distros","images","systems","profiles","links"] : + if not x in ["webui", "localmirror","repo_mirror","ks_mirror","kickstarts","kickstarts_sys","distros","images","systems","profiles","links"] : # delete directories that shouldn't exist self.rmtree(path) if x in ["kickstarts","kickstarts_sys","images","systems","distros","profiles"]: diff --git a/cobbler/webui/CobblerWeb.py b/cobbler/webui/CobblerWeb.py new file mode 100644 index 0000000..4ed8d39 --- /dev/null +++ b/cobbler/webui/CobblerWeb.py @@ -0,0 +1,191 @@ +#!/usr/bin/env python + +import xmlrpclib +from Cheetah.Template import Template +import os + +class CobblerWeb(object): + def __init__(self, server=None, base_url='/'): + self.server = server + self.base_url = base_url + + def xmlrpc(self): + return xmlrpclib.ServerProxy(self.server, allow_none=True) + + def __render(self, template, data): + data['base_url'] = self.base_url + #filepath = "%s/%s" % (os.path.dirname(__file__), template) + filepath = os.path.join("/usr/share/cobbler/webui_templates/",template) + tmpl = Template( file=filepath, searchList=data ) + return str(tmpl) + + def modes(self): + retval = list() + for m in dir(self): + func = getattr( self, m ) + if hasattr(func, 'exposed') and getattr(func,'exposed'): + retval.append(m) + return retval + + # ------------------------------------------------------------------------ # + # Index + # ------------------------------------------------------------------------ # + def index(self): + return self.__render( 'index.tmpl', dict() ) + + # ------------------------------------------------------------------------ # + # Settings + # ------------------------------------------------------------------------ # + def settings_view(self): + return self.__render( 'item.tmpl', { + 'item_data': self.xmlrpc().get_settings(), + 'caption': "Cobbler Settings" + } ) + + # ------------------------------------------------------------------------ # + # Distributions + # ------------------------------------------------------------------------ # + def distro_view(self, distribution): + # get_distro_for_koan() flattens out the inherited options + #distro = self.xmlrpc().get_distro_for_koan(distribution) + return self.__render( 'item.tmpl', { + 'item_data': self.xmlrpc().get_distro(distribution), + 'caption': "Distribution \"%s\" Details" % distribution + } ) + + def distro_list(self): + return self.__render( 'distro_list.tmpl', { + 'distros': self.xmlrpc().get_distros() + } ) + + # ------------------------------------------------------------------------ # + # Systems + # ------------------------------------------------------------------------ # + # if the system list is huge, this will probably need to use an + # iterator so the list doesn't get copied around + def system_list(self): + return self.__render( 'system_list.tmpl', { + 'systems': self.xmlrpc().get_systems() + } ) + + def system_add(self): + return self.__render( 'system_edit.tmpl', { + 'profiles': self.xmlrpc().get_profiles() + } ) + + def system_view(self, name): + return self.__render( 'item.tmpl', { + 'item_data': self.xmlrpc().get_profile(name), + 'caption': "Profile %s Settings" % name + } ) + + def system_save(self, name, profile, submit, new_or_edit, mac=None, ip=None, hostname=None, kopts=None, ksmeta=None, netboot='n'): + # parameter checking + if name is None: + return self.error_page("System name parameter is REQUIRED.") + + if mac is None and ip is None and hostname is None: + return self.error_page("System must have at least one of MAC/IP/hostname.") + + # resolve_ip, is_mac, and is_ip are from cobbler.utils + if hostname and not ip: + ip = resolve_ip( hostname ) + + if mac and not is_mac( mac ): + return self.error_page("The provided MAC address appears to be invalid.") + + if ip and not is_ip( ip ): + return self.error_page("The provided IP address appears to be invalid.") + + if new_or_edit == "edit": + system = self.xmlrpc().get_system(name) + else: + # FIXME: convert to r/w xmlrpc + system = None + #system = self.api.new_system() + system.set_name( name ) + self.api.systems().add( system ) + + system.set_profile( profile ) + + return self.__render( 'item.tmpl', { + 'item_data': system, + 'caption': "Profile %s Settings" % name + } ) + + def system_edit(self, name): + return self.__render( 'system_edit.tmpl', { + 'system': self.xmlrpc().get_system(name), + 'profiles': self.xmlrpc().get_profiles() + } ) + + # ------------------------------------------------------------------------ # + # Profiles + # ------------------------------------------------------------------------ # + def profile_list(self): + return self.__render( 'profile_list.tmpl', { + 'profiles': self.xmlrpc().get_profiles() + } ) + + def profile_add(self): + return self.__render( 'profile_add.tmpl', { + 'distros': self.xmlrpc().get_distros(), + 'ksfiles': self.__ksfiles() + } ) + + def profile_save(self): + pass + + # ------------------------------------------------------------------------ # + # Kickstart files + # ------------------------------------------------------------------------ # + def ksfile_list(self): + return self.__render( 'ksfile_list.tmpl', { + 'ksfiles': self.__ksfiles() + } ) + + def ksfile_view(self, ksfile): + return self.__render( 'ksfile_view.tmpl', { + 'ksdata': self.__ksfile_data( ksfile ), + 'ksfile': ksfile + } ) + + def __ksfiles(self): + ksfiles = list() + for profile in self.xmlrpc().get_profiles(): + ksfile = profile['kickstart'] + if not ksfile in ksfiles: + ksfiles.append( ksfile ) + return ksfiles + + def __ksfile_data(self, ksfile): + pass + + # ------------------------------------------------------------------------ # + # Miscellaneous + # ------------------------------------------------------------------------ # + def error_page(self, message): + return self.__render( 'error_page.tmpl', { + 'message': message + } ) + + # make CherryPy and related frameworks able to use this module easily + # by borrowing the 'exposed' function attritbute standard and using + # it for the modes() method + modes.exposed = False + error_page.exposed = False + distro_list.exposed = True + distro_view.exposed = True + index.exposed = True + profile_add.exposed = True + profile_list.exposed = True + profile_save.exposed = True + settings_view.exposed = True + system_add.exposed = True + system_edit.exposed = True + system_list.exposed = True + system_save.exposed = True + system_view.exposed = True + ksfile_view.exposed = True + ksfile_list.exposed = True + diff --git a/cobbler/webui/__init__.py b/cobbler/webui/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cobbler/webui/master.py b/cobbler/webui/master.py new file mode 100644 index 0000000..58b5047 --- /dev/null +++ b/cobbler/webui/master.py @@ -0,0 +1,240 @@ +#!/usr/bin/env python + + + + +################################################## +## DEPENDENCIES +import sys +import os +import os.path +from os.path import getmtime, exists +import time +import types +import __builtin__ +from Cheetah.Version import MinCompatibleVersion as RequiredCheetahVersion +from Cheetah.Version import MinCompatibleVersionTuple as RequiredCheetahVersionTuple +from Cheetah.Template import Template +from Cheetah.DummyTransaction import DummyTransaction +from Cheetah.NameMapper import NotFound, valueForName, valueFromSearchList, valueFromFrameOrSearchList +from Cheetah.CacheRegion import CacheRegion +import Cheetah.Filters as Filters +import Cheetah.ErrorCatchers as ErrorCatchers + +################################################## +## MODULE CONSTANTS +try: + True, False +except NameError: + True, False = (1==1), (1==0) +VFFSL=valueFromFrameOrSearchList +VFSL=valueFromSearchList +VFN=valueForName +currentTime=time.time +__CHEETAH_version__ = '2.0rc8' +__CHEETAH_versionTuple__ = (2, 0, 0, 'candidate', 8) +__CHEETAH_genTime__ = 1189393696.009383 +__CHEETAH_genTimestamp__ = 'Sun Sep 9 20:08:16 2007' +__CHEETAH_src__ = 'master.tmpl' +__CHEETAH_srcLastModified__ = 'Sun Sep 9 20:02:36 2007' +__CHEETAH_docstring__ = 'Autogenerated by CHEETAH: The Python-Powered Template Engine' + +if __CHEETAH_versionTuple__ < RequiredCheetahVersionTuple: + raise AssertionError( + 'This template was compiled with Cheetah version' + ' %s. Templates compiled before version %s must be recompiled.'%( + __CHEETAH_version__, RequiredCheetahVersion)) + +################################################## +## CLASSES + +class master(Template): + + ################################################## + ## CHEETAH GENERATED METHODS + + + def __init__(self, *args, **KWs): + + Template.__init__(self, *args, **KWs) + if not self._CHEETAH__instanceInitialized: + cheetahKWArgs = {} + allowedKWs = 'searchList namespaces filter filtersLib errorCatcher'.split() + for k,v in KWs.items(): + if k in allowedKWs: cheetahKWArgs[k] = v + self._initCheetahInstance(**cheetahKWArgs) + + + def body(self, **KWS): + + + + ## CHEETAH: generated from #block body at line 46, col 1. + trans = KWS.get("trans") + if (not trans and not self._CHEETAH__isBuffering and not callable(self.transaction)): + trans = self.transaction # is None unless self.awake() was called + if not trans: + trans = DummyTransaction() + _dummyTrans = True + else: _dummyTrans = False + write = trans.response().write + SL = self._CHEETAH__searchList + _filter = self._CHEETAH__currentFilter + + ######################################## + ## START - generated method body + + write(''' +

Template Failure

+ +''') + + ######################################## + ## END - generated method body + + return _dummyTrans and trans.response().getvalue() or "" + + + def respond(self, trans=None): + + + + ## CHEETAH: main method generated for this template + if (not trans and not self._CHEETAH__isBuffering and not callable(self.transaction)): + trans = self.transaction # is None unless self.awake() was called + if not trans: + trans = DummyTransaction() + _dummyTrans = True + else: _dummyTrans = False + write = trans.response().write + SL = self._CHEETAH__searchList + _filter = self._CHEETAH__currentFilter + + ######################################## + ## START - generated method body + + write(''' + + + ''') + _v = VFFSL(SL,"title",True) # '$title' on line 5, col 12 + if _v is not None: write(_filter(_v, rawExpr='$title')) # from line 5, col 12. + write(''' + + + + + + + + +
+

+ + Cobbler Logo + +

+
+ +
+ + + +
+''') + self.body(trans=trans) + write('''
+
+ + + +''') + + ######################################## + ## END - generated method body + + return _dummyTrans and trans.response().getvalue() or "" + + ################################################## + ## CHEETAH GENERATED ATTRIBUTES + + + _CHEETAH__instanceInitialized = False + + _CHEETAH_version = __CHEETAH_version__ + + _CHEETAH_versionTuple = __CHEETAH_versionTuple__ + + _CHEETAH_genTime = __CHEETAH_genTime__ + + _CHEETAH_genTimestamp = __CHEETAH_genTimestamp__ + + _CHEETAH_src = __CHEETAH_src__ + + _CHEETAH_srcLastModified = __CHEETAH_srcLastModified__ + + title = "Cobbler Web Interface" + + _mainCheetahMethod_for_master= 'respond' + +## END CLASS DEFINITION + +if not hasattr(master, '_initCheetahAttributes'): + templateAPIClass = getattr(master, '_CHEETAH_templateClass', Template) + templateAPIClass._addCheetahPlumbingCodeToClass(master) + + +# CHEETAH was developed by Tavis Rudd and Mike Orr +# with code, advice and input from many other volunteers. +# For more information visit http://www.CheetahTemplate.org/ + +################################################## +## if run from command line: +if __name__ == '__main__': + from Cheetah.TemplateCmdLineIface import CmdLineIface + CmdLineIface(templateObj=master()).run() + + diff --git a/cobbler/webui/webui-cgi.py b/cobbler/webui/webui-cgi.py new file mode 100755 index 0000000..42387bd --- /dev/null +++ b/cobbler/webui/webui-cgi.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python + +import cgi +import cgitb +import wsgiref +import os +import sys +from cobbler.webui.CobblerWeb import CobblerWeb + +def map_modes(): + path = os.environ.get( 'PATH_INFO', 'index' ) + + if path.startswith('/'): + path = path[1:] + if path.endswith('/'): + path = path[:-1] + + if path is '': + path = 'index' + + return path + +def base_url(): + return os.environ.get('SCRIPT_NAME', '') + +def main(): + print "Content-type: text/html" + print + + path = map_modes() + form = cgi.parse() + + # ditch single-element arrays in the 'form' dictionary + # - may be bad for form elements that are sometimes lists and sometimes + # single items + for key,val in form.items(): + if isinstance(val, list): + if len(val) == 1: + form[key] = val[0] + + cw = CobblerWeb( server="http://localhost:25151", base_url=base_url() ) + + if path in cw.modes(): + func = getattr( cw, path ) + print func( **form ) + +main() diff --git a/cobbler/webui/webui-cherrypy3.py b/cobbler/webui/webui-cherrypy3.py new file mode 100755 index 0000000..fff5e77 --- /dev/null +++ b/cobbler/webui/webui-cherrypy3.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python + +from CobblerWeb import CobblerWeb +import cherrypy + +cherrypy.tree.mount( CobblerWeb(server="http://localhost:25151", base_url=''), script_name='/', config='webui-cherrypy.cfg' ) +cherrypy.server.quickstart() +cherrypy.engine.start() +cherrypy.engine.block() + -- cgit