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. --- MANIFEST.in | 4 + Makefile | 2 +- cobbler.spec | 9 ++ 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 ++ config/webui-cherrypy.cfg | 9 ++ scripts/cobbler_webui.cgi | 47 ++++++++ setup.py | 64 +++++++++- webui_content/cobblerweb.css | 34 ++++++ webui_content/logo-cobbler.png | Bin 0 -> 15085 bytes webui_content/style.css | 183 +++++++++++++++++++++++++++++ webui_templates/distro_list.tmpl | 35 ++++++ webui_templates/error_page.tmpl | 6 + webui_templates/index.tmpl | 8 ++ webui_templates/item.tmpl | 38 ++++++ webui_templates/ksfile_edit.tmpl | 17 +++ webui_templates/ksfile_list.tmpl | 33 ++++++ webui_templates/ksfile_view.tmpl | 6 + webui_templates/master.tmpl | 55 +++++++++ webui_templates/profile_add.tmpl | 45 +++++++ webui_templates/profile_list.tmpl | 33 ++++++ webui_templates/system_edit.tmpl | 48 ++++++++ webui_templates/system_list.tmpl | 35 ++++++ 27 files changed, 1194 insertions(+), 7 deletions(-) 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 create mode 100644 config/webui-cherrypy.cfg create mode 100755 scripts/cobbler_webui.cgi create mode 100644 webui_content/cobblerweb.css create mode 100644 webui_content/logo-cobbler.png create mode 100644 webui_content/style.css create mode 100644 webui_templates/distro_list.tmpl create mode 100644 webui_templates/error_page.tmpl create mode 100644 webui_templates/index.tmpl create mode 100644 webui_templates/item.tmpl create mode 100644 webui_templates/ksfile_edit.tmpl create mode 100644 webui_templates/ksfile_list.tmpl create mode 100644 webui_templates/ksfile_view.tmpl create mode 100644 webui_templates/master.tmpl create mode 100644 webui_templates/profile_add.tmpl create mode 100644 webui_templates/profile_list.tmpl create mode 100644 webui_templates/system_edit.tmpl create mode 100644 webui_templates/system_list.tmpl diff --git a/MANIFEST.in b/MANIFEST.in index 38dabf4..8f9588c 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -8,6 +8,7 @@ include config/cobblerd_rotate include config/cobbler_hosts include config/modules.conf include config/auth.conf +include config/webui-cherrypy.cfg recursive-include templates *.template recursive-include kickstarts *.ks include docs/cobbler.1.gz @@ -16,6 +17,9 @@ include scripts/watcher.py include scripts/cobblerd include scripts/findks.cgi include scripts/nopxe.cgi +include scripts/cobbler_webui.cgi include snippets/* recursive-include po *.pot recursive-include po *.po +recursive-include webui_content * +recursive-include webui_templates * diff --git a/Makefile b/Makefile index aa14401..4d162f3 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,7 @@ test: install build: clean messages python setup.py build -f -install: clean +install: clean manpage python setup.py install -f sdist: clean messages diff --git a/cobbler.spec b/cobbler.spec index 9db884a..00aa7a5 100644 --- a/cobbler.spec +++ b/cobbler.spec @@ -72,6 +72,9 @@ test "x$RPM_BUILD_ROOT" != "x" && rm -rf $RPM_BUILD_ROOT %defattr(2744,apache,apache) /var/www/cgi-bin/findks.cgi /var/www/cgi-bin/nopxe.cgi +/var/www/cgi-bin/cobbler_webui.cgi +%dir /usr/share/cobbler/webui_templates +/usr/share/cobbler/webui_templates/*.tmpl %dir /var/log/cobbler %dir /var/log/cobbler/kicklog %dir /var/www/cobbler/ @@ -86,6 +89,9 @@ test "x$RPM_BUILD_ROOT" != "x" && rm -rf $RPM_BUILD_ROOT %dir /var/www/cobbler/profiles %dir /var/www/cobbler/systems %dir /var/www/cobbler/links +%dir /var/www/cobbler/webui +/var/www/cobbler/webui/*.css +/var/www/cobbler/webui/*.png %defattr(-,root,root) %dir /tftpboot/pxelinux.cfg %dir /tftpboot/images @@ -105,12 +111,15 @@ test "x$RPM_BUILD_ROOT" != "x" && rm -rf $RPM_BUILD_ROOT %config(noreplace) /etc/cobbler/rsync.exclude %config(noreplace) /etc/logrotate.d/cobblerd_rotate %config(noreplace) /etc/cobbler/modules.conf +%config(noreplace) /etc/cobbler/webui-cherrypy.cfg %dir %{python_sitelib}/cobbler %dir %{python_sitelib}/cobbler/yaml %dir %{python_sitelib}/cobbler/modules +%dir %{python_sitelib}/cobbler/webui %{python_sitelib}/cobbler/*.py* %{python_sitelib}/cobbler/yaml/*.py* %{python_sitelib}/cobbler/modules/*.py* +%{python_sitelib}/cobbler/webui/*.py* %{_mandir}/man1/cobbler.1.gz /etc/init.d/cobblerd %config(noreplace) /etc/httpd/conf.d/cobbler.conf 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() + diff --git a/config/webui-cherrypy.cfg b/config/webui-cherrypy.cfg new file mode 100644 index 0000000..5d7dca9 --- /dev/null +++ b/config/webui-cherrypy.cfg @@ -0,0 +1,9 @@ +[global] +base_url_filter.on = True +server.thread_pool = 10 + +[/static] +tools.staticdir.on = True +tools.staticdir.dir = "webui" +tools.staticdir.root = "/var/www/cobbler" + diff --git a/scripts/cobbler_webui.cgi b/scripts/cobbler_webui.cgi new file mode 100755 index 0000000..42387bd --- /dev/null +++ b/scripts/cobbler_webui.cgi @@ -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/setup.py b/setup.py index c99ccfc..0e76cd8 100644 --- a/setup.py +++ b/setup.py @@ -17,11 +17,13 @@ if __name__ == "__main__": etcpath = "/etc/cobbler/" wwwconf = "/etc/httpd/conf.d/" wwwpath = "/var/www/cobbler/" + wwwgfx = "/var/www/cobbler/webui/" initpath = "/etc/init.d/" logpath = "/var/log/cobbler/" logpath2 = "/var/log/cobbler/kicklog" logpath3 = "/var/log/cobbler/syslog" snippets = "/var/lib/cobbler/snippets" + wwwtmpl = "/usr/share/cobbler/webui_templates/" vw_localmirror = "/var/www/cobbler/localmirror" vw_kickstarts = "/var/www/cobbler/kickstarts" vw_kickstarts_sys = "/var/www/cobbler/kickstarts_sys" @@ -48,34 +50,55 @@ if __name__ == "__main__": "cobbler", "cobbler/yaml", "cobbler/modules", + "cobbler/webui", ], scripts = ["scripts/cobbler", "scripts/cobblerd"], data_files = [ + + # cgi files (cgipath, ['scripts/findks.cgi', 'scripts/nopxe.cgi']), + (cgipath, ['scripts/cobbler_webui.cgi']), + + # miscellaneous config files (rotpath, ['config/cobblerd_rotate']), (wwwconf, ['config/cobbler.conf']), + (cobpath, ['config/cobbler_hosts']), + (etcpath, ['config/modules.conf']), + (etcpath, ['config/auth.conf']), + (etcpath, ['config/webui-cherrypy.cfg']), + (etcpath, ['config/rsync.exclude']), + (initpath, ['config/cobblerd']), + + # bootloaders and syslinux support files (cobpath, ['loaders/elilo-3.6-ia64.efi']), (cobpath, ['loaders/menu.c32']), - (cobpath, ['config/cobbler_hosts']), + + # sample kickstart files (etcpath, ['kickstarts/kickstart_fc5.ks']), (etcpath, ['kickstarts/kickstart_fc6.ks']), (etcpath, ['kickstarts/kickstart_fc6_domU.ks']), (etcpath, ['kickstarts/default.ks']), + + # templates for DHCP and syslinux configs (etcpath, ['templates/dhcp.template']), (etcpath, ['templates/dnsmasq.template']), (etcpath, ['templates/pxedefault.template']), (etcpath, ['templates/pxesystem.template']), (etcpath, ['templates/pxesystem_ia64.template']), (etcpath, ['templates/pxeprofile.template']), - (etcpath, ['config/modules.conf']), - (etcpath, ['config/auth.conf']), + + # useful kickstart snippets that we ship (snippets, ['snippets/partition_select']), + + # documentation (manpath, ['docs/cobbler.1.gz']), - (etcpath, ['config/rsync.exclude']), - (initpath, ['config/cobblerd']), + + # logfiles (logpath, []), (logpath2, []), (logpath3, []), + + # web page directories that we own (vw_localmirror, []), (vw_kickstarts, []), (vw_kickstarts_sys, []), @@ -87,8 +110,39 @@ if __name__ == "__main__": (vw_systems, []), (vw_profiles, []), (vw_links, []), + + # tftp directories that we own (tftp_cfg, []), (tftp_images, []), + + # Web UI templates for object viewing & modification + # FIXME: other templates to add as they are created. + # slurp in whole directory? + (wwwtmpl, ['webui_templates/distro_list.tmpl']), + (wwwtmpl, ['webui_templates/profile_list.tmpl']), + (wwwtmpl, ['webui_templates/profile_add.tmpl']), + (wwwtmpl, ['webui_templates/system_list.tmpl']), + (wwwtmpl, ['webui_templates/system_edit.tmpl']), + #(wwwtmpl, ['webui_templates/repo_list.tmpl']), + + # Web UI common templates + (wwwtmpl, ['webui_templates/error_page.tmpl']), + (wwwtmpl, ['webui_templates/master.tmpl']), + (wwwtmpl, ['webui_templates/item.tmpl']), + (wwwtmpl, ['webui_templates/index.tmpl']), + + # Web UI kickstart file editing + (wwwtmpl, ['webui_templates/ksfile_edit.tmpl']), + (wwwtmpl, ['webui_templates/ksfile_list.tmpl']), + (wwwtmpl, ['webui_templates/ksfile_view.tmpl']), + + # Web UI support files + (wwwgfx, []), + (wwwgfx, ['webui_content/style.css']), + (wwwgfx, ['webui_content/logo-cobbler.png']), + (wwwgfx, ['webui_content/cobblerweb.css']), + + # Directories to hold cobbler triggers ("/var/lib/cobbler/triggers/add/distro/pre", []), ("/var/lib/cobbler/triggers/add/distro/post", []), ("/var/lib/cobbler/triggers/add/profile/pre", []), diff --git a/webui_content/cobblerweb.css b/webui_content/cobblerweb.css new file mode 100644 index 0000000..806725b --- /dev/null +++ b/webui_content/cobblerweb.css @@ -0,0 +1,34 @@ + +fieldset#cform label { + display: block; + width: 150px; + margin-bottom: 10px; + float: left; + padding-right: 4em; +} + +pre.config_data { + font-family: monospace; + background: white; + color: black; + width: 640px; + min-height: 480px; +} + +br { + clear: both; +} + +tr.rowodd { background-color: #212121; } +tr.roweven { background-color: #3f3d3d; } + +table.sortable th { + background-color: #363a4e; + margin: 0; +} + +table.sortable caption { + background-color: #202331; + margin: 0; +} + diff --git a/webui_content/logo-cobbler.png b/webui_content/logo-cobbler.png new file mode 100644 index 0000000..f8fe53c Binary files /dev/null and b/webui_content/logo-cobbler.png differ diff --git a/webui_content/style.css b/webui_content/style.css new file mode 100644 index 0000000..d1aabe0 --- /dev/null +++ b/webui_content/style.css @@ -0,0 +1,183 @@ +body, html { + background-color: black; + margin: 0; + padding: 0; +} + +body { + min-width: 750px; +} + +img { + border: none; +} + +/* site-wide font specifications */ +body, ul, li, p, h1, h2, h3 { + font-family: "Liberation Sans", "Helvetica", "Luxi Sans", "Bitstream Vera Sans", sans-serif; + color: white; +} + +ul { + list-style-type: square; +} + +#sidebar { + height: auto; + float: left; +} + +#sidebar p, #sidebar h2 { + margin-left: 24px; +} + +ul#nav { + text-transform: uppercase; + letter-spacing: -.05em; + list-style-type: none; + font-family: "URW Gothic", "Liberation Sans", "Helvetica", "Luxi Sans", "Bitstream Vera Sans", sans-serif; +} + +ul#nav li a { + color: #59cbe1; + text-decoration: none; +} + +ul#nav li#active { + list-style-image: url('http://cobbler.et.redhat.com/img/current-page.png'); +} + +ul#nav li#active a, ul#nav li#active a:link, ul#nav li#active a:visited { + color: white; +} + +div#feed { + width: 160px; + font-size: small; + margin-top: 28px; + margin-bottom: 50px; + border-top: 1px solid #444; + padding-top: 5px; +} + +div#feed ul { + font-size: x-small; +} + + +#wrap { + min-width: 750px; + margin: 0px 3%; + padding-top: 12px; +} + +div#main { + background-color: #212121; + border-top: 1px solid #59cbe1; + border-bottom: 1px solid #59cbe1; + overflow: auto; + padding: 20px 0px; +} + +div#content { + width: 70%; + float: right; + padding: 0px 24px; +} + +div#content h1 { + font-size: x-large; + font-weight: normal; +} + +div#content h2 { + font-size: medium; + font-weight: 900; + text-transform: uppercase; +} + +div#content h3 { + color: #59cbe1; +} + + +div#content p { + font-size: small; + color: #ccc; +} + +a:link { + color: #59cbe1; +} + +a:hover { + color: white; +} + +a:visited { + color: #99d9e8; +} + + +#content p.metadata { + font-size: x-small; + color: white; +} + +dt { + font-weight: 900; + margin-bottom: 1em; +} + +dd { + color: #ccc; + margin-bottom: 2em; +} + +.back-to-top { + background: url('http://cobbler.et.redhat.com/img/up-arrow.gif') left no-repeat; + padding-left: 20px; + font-size: small; +} + +blockquote { + border-top: 1px solid #ccc; + border-bottom: 1px solid #ccc; + background: #444; + padding: 2px 4px; +} + +p.note, p.tip { + margin: 16px 8px; + padding: 8px 12px; + color: white !important; + background-color: #666; + -moz-border-radius: 10px; +} + +p.note strong, p.tip strong { + font-size: 120%; +} + +tt { + font-size: 130%; + font-weight: 700; +} + +#faq, #toc { + border-top: 1px solid #ccc; + border-bottom: 1px solid #ccc; + background: #444; + padding-top: 12px; + padding-bottom: 16px; + padding-left: 30px; +} + +#footer { + width: 100%; + font-size: x-small; + color: #aaa; + text-align: center; + padding-top: 16px; + padding-bottom: 16px; +} diff --git a/webui_templates/distro_list.tmpl b/webui_templates/distro_list.tmpl new file mode 100644 index 0000000..4654972 --- /dev/null +++ b/webui_templates/distro_list.tmpl @@ -0,0 +1,35 @@ +#extends cobbler.webui.master +#attr $title = "Cobbler: List of Distributions" + +#block body + + + + + + + + + + + #set $evenodd = 1 + #for $distro in $distros + #if $evenodd % 2 == 0 + #set $tr_class = "roweven" + #else + #set $tr_class = "rowodd" + #end if + #set $evenodd += 1 + + + + + + + #end for + +
Cobbler Distributions
NameBreedArch
+ $distro.name + $distro.breed$distro.arch
+#end block body + diff --git a/webui_templates/error_page.tmpl b/webui_templates/error_page.tmpl new file mode 100644 index 0000000..9bf7473 --- /dev/null +++ b/webui_templates/error_page.tmpl @@ -0,0 +1,6 @@ +#extends cobbler.webui.master +#attr $title = "Cobbler: Error" + +#block body +

$message

+#end block body diff --git a/webui_templates/index.tmpl b/webui_templates/index.tmpl new file mode 100644 index 0000000..b249f32 --- /dev/null +++ b/webui_templates/index.tmpl @@ -0,0 +1,8 @@ +#extends cobbler.webui.master + +#block body + +This is my empty index page. + +#end block body + diff --git a/webui_templates/item.tmpl b/webui_templates/item.tmpl new file mode 100644 index 0000000..e06083f --- /dev/null +++ b/webui_templates/item.tmpl @@ -0,0 +1,38 @@ +#extends cobbler.webui.master + +#block body + + + + + + + + #set $evenodd = 1 + #for $key,$value in $item_data.items(): + + #if $evenodd % 2 == 0 + #set $tr_class = "roweven" + #else + #set $tr_class = "rowodd" + #end if + #set $evenodd += 1 + + + + + + #end for + +
$caption
KeyValue
$key + #if isinstance($value,dict): +
    + #for $skey,$sval in $value.items(): +
  • $skey = $sval
  • + #end for +
+ #else + $value + #end if +
+#end block body diff --git a/webui_templates/ksfile_edit.tmpl b/webui_templates/ksfile_edit.tmpl new file mode 100644 index 0000000..a28fe74 --- /dev/null +++ b/webui_templates/ksfile_edit.tmpl @@ -0,0 +1,17 @@ +#extends cobbler.webui.master +#attr $title = "Cobbler: Edit Kickstart File $ksfile" + +#block body +
+ +
+ Edit Kickstart File + +
+
+ + + +
+
+#end block body diff --git a/webui_templates/ksfile_list.tmpl b/webui_templates/ksfile_list.tmpl new file mode 100644 index 0000000..af196ec --- /dev/null +++ b/webui_templates/ksfile_list.tmpl @@ -0,0 +1,33 @@ +#extends cobbler.webui.master + +#block body + + + + + + + + + + #set $evenodd = 1 + #for $ksfile in $ksfiles + #if $evenodd % 2 == 0 + #set $tr_class = "roweven" + #else + #set $tr_class = "rowodd" + #end if + #set $evenodd += 1 + + + + + + #end for + +
Cobbler Kickstart Files
Name
$ksfile +
+#end block body diff --git a/webui_templates/ksfile_view.tmpl b/webui_templates/ksfile_view.tmpl new file mode 100644 index 0000000..b6abf67 --- /dev/null +++ b/webui_templates/ksfile_view.tmpl @@ -0,0 +1,6 @@ +#extends cobbler.webui.master + +#block body +
$ksdata
+#end block body + diff --git a/webui_templates/master.tmpl b/webui_templates/master.tmpl new file mode 100644 index 0000000..5517d9c --- /dev/null +++ b/webui_templates/master.tmpl @@ -0,0 +1,55 @@ + + + +#attr $title = "Cobbler Web Interface" + $title + + + + + + + + +
+

+ + Cobbler Logo + +

+
+ +
+ + + +
+#block body + +

Template Failure

+ +#end block body +
+
+ + + diff --git a/webui_templates/profile_add.tmpl b/webui_templates/profile_add.tmpl new file mode 100644 index 0000000..424e7d9 --- /dev/null +++ b/webui_templates/profile_add.tmpl @@ -0,0 +1,45 @@ +#extends cobbler.webui.master + +#block body +
+
+ Add a Profile + + + + + +
+ + + +
+ + + +
+ + + +
+ + + +
+ + + +
+#end block body diff --git a/webui_templates/profile_list.tmpl b/webui_templates/profile_list.tmpl new file mode 100644 index 0000000..8699b40 --- /dev/null +++ b/webui_templates/profile_list.tmpl @@ -0,0 +1,33 @@ +#extends cobbler.webui.master + +#block body + + + + + + + + + + + #set $evenodd = 1 + #for $profile in $profiles + #if $evenodd % 2 == 0 + #set $tr_class = "roweven" + #else + #set $tr_class = "rowodd" + #end if + #set $evenodd += 1 + + + + + + + #end for + +
Cobbler Profiles
NameDistributionKS File
$profile.name + $profile.distro + $profile.kickstart
+#end block body diff --git a/webui_templates/system_edit.tmpl b/webui_templates/system_edit.tmpl new file mode 100644 index 0000000..8034bd2 --- /dev/null +++ b/webui_templates/system_edit.tmpl @@ -0,0 +1,48 @@ +#extends cobbler.webui.master + +#block body + +
+ Add a System + + + +
+ + + +
+ + + +
+ + + +
+ + + +
+ + + +
+ + + +
+ + + Yes No +
+ + + +
+
+#end block body diff --git a/webui_templates/system_list.tmpl b/webui_templates/system_list.tmpl new file mode 100644 index 0000000..8e6d1d8 --- /dev/null +++ b/webui_templates/system_list.tmpl @@ -0,0 +1,35 @@ +#extends cobbler.webui.master + +#block body + + + + + + + + + + + #set $evenodd = 1 + #for $system in $systems + #if $evenodd % 2 == 0 + #set $tr_class = "roweven" + #else + #set $tr_class = "rowodd" + #end if + #set $evenodd += 1 + + + + + + + #end for + +
Cobbler Systems
NameDistribution
${system.name} + ${system.distro} + + edit +
+#end block body -- cgit