summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile3
-rw-r--r--cobbler/remote.py21
-rw-r--r--cobbler/webui/CobblerWeb.py168
-rw-r--r--docs/cobbler.pod6
-rw-r--r--webui_content/cobblerweb.css10
-rw-r--r--webui_templates/ksfile_edit.tmpl4
-rw-r--r--webui_templates/login.tmpl2
-rw-r--r--webui_templates/repo_edit.tmpl18
-rw-r--r--webui_templates/system_edit.tmpl27
9 files changed, 182 insertions, 77 deletions
diff --git a/Makefile b/Makefile
index 85189d3..c605f40 100644
--- a/Makefile
+++ b/Makefile
@@ -8,6 +8,9 @@ clean:
-rm -rf cobbler-* dist build
-rm -rf *~
-rm -rf rpm-build/
+ -rm -f docs/cobbler.1.gz
+ -rm -f docs/cobbler.html
+ -rm -f po/messages.pot*
manpage:
pod2man --center="cobbler" --release="" ./docs/cobbler.pod | gzip -c > ./docs/cobbler.1.gz
diff --git a/cobbler/remote.py b/cobbler/remote.py
index c814a6e..f3fa070 100644
--- a/cobbler/remote.py
+++ b/cobbler/remote.py
@@ -259,6 +259,27 @@ class CobblerXMLRPCInterface:
return self._fix_none(utils.blender(True, obj))
return self._fix_none({})
+ def get_random_mac(self):
+ """
+ Generate a random MAC address.
+ from xend/server/netif.py
+ Generate a random MAC address.
+ Uses OUI 00-16-3E, allocated to
+ Xensource, Inc. Last 3 fields are random.
+ return: MAC address string
+ """
+ self._refresh()
+ mac = [ 0x00, 0x16, 0x3e,
+ random.randint(0x00, 0x7f),
+ random.randint(0x00, 0xff),
+ random.randint(0x00, 0xff) ]
+ mac = ':'.join(map(lambda x: "%02x" % x, mac))
+ systems = self.api.systems()
+ while ( systems.find(mac=mac) ):
+ mac = self.random_mac()
+
+ return mac
+
def _fix_none(self,data):
"""
Convert None in XMLRPC to just '~'. The above
diff --git a/cobbler/webui/CobblerWeb.py b/cobbler/webui/CobblerWeb.py
index abbc5aa..f4dcaee 100644
--- a/cobbler/webui/CobblerWeb.py
+++ b/cobbler/webui/CobblerWeb.py
@@ -21,8 +21,10 @@ import sys
import Cookie
import time
-# set up logging
+COOKIE_TIMEOUT=29*60
+INVALID_CREDS="Invalid credentials. Please log in."
+# set up logging
logger = logging.getLogger("cobbler.webui")
logger.setLevel(logging.DEBUG)
ch = logging.FileHandler("/var/log/cobbler/webui.log")
@@ -78,12 +80,14 @@ class CobblerWeb(object):
self.remote.token_check(self.token)
return True
except Exception, e:
- # FIXME: check exception type to see that it is login related
- logger.info("token timeout for: %s" % self.username)
- log_exc()
- self.token = None
- # this should put us back to the login screen
- self.__cookie_logout()
+ if str(e).find("invalid token") != -1:
+ logger.info("token timeout for: %s" % self.username)
+ log_exc()
+ self.token = None
+ # this should put us back to the login screen
+ self.__cookie_logout()
+ else:
+ raise e
# if we (still) don't have a token, login for the first time
if self.token is None and is_login:
@@ -121,22 +125,71 @@ class CobblerWeb(object):
tmpl = Template( file=filepath, searchList=data )
return str(tmpl)
+ def __truth(self,value):
+ """
+ Convert 0/1, True/False, "true"/"false", etc. to Python booleans.
+ """
+
+ if value is None:
+ return False
+
+ if type(value) == type(bool()):
+ return value
+
+ if type(value) == type(int()):
+ if value > 0:
+ return True
+ else:
+ return False
+
+ # from item_repo.py
+ if not str(value).lower() in ["yes","y","yup","yeah","true"]:
+ return False
+ else:
+ return True
+
+ raise Exception("Could not determine truth of value %s" % str(value))
+
def cookies(self):
"""
Returns a Cookie.SimpleCookie object with all of CobblerWeb's cookies.
Mmmmm cookies!
"""
+ # The browser doesn't maintain expires for us, which is fine since
+ # cobblerd will continue to refresh a token as long as it's being
+ # accessed.
+ if self.token and self.__cookies["cobbler_xmlrpc_token"]:
+ self.__setcookie( self.token, COOKIE_TIMEOUT )
+
return self.__cookies
- def __cookie_logout(self,):
- self.__cookies["cobbler_xmlrpc_token"] = "null"
- # self.__cookies["cobbler_xmlrpc_token"]['expires'] = time.time() - 9999
- return self.cookies
+ def __setcookie(self,token,exp_offset):
+ """
+ Does all of the cookie setting in one place.
+ """
+ # HTTP cookie RFC:
+ # http://www.w3.org/Protocols/rfc2109/rfc2109
+ #
+ # Cookie.py does not let users explicitely set cookies' expiration time.
+ # Instead, it runs the 'expires' member of the dictionary through its
+ # _getdate() function. As of this writing, the signature is:
+ # _getdate(future=0, weekdayname=_weekdayname, monthname=_monthname)
+ # When it is called to generate output, the value of 'expires' is passed
+ # in as a _positional_ parameter in the first slot.
+ # In order to get a time in the past, it appears that a negative number
+ # can be passed through, which is what we do here.
+ self.__cookies["cobbler_xmlrpc_token"] = token
+ self.__cookies["cobbler_xmlrpc_token"]['expires'] = exp_offset
+
+ def __cookie_logout(self):
+ # set the cookie's expiration to this time, yesterday, which results
+ # in it being deleted
+ self.__setcookie( 'null', -86400 )
+ return self.__cookies
def __cookie_login(self,token):
- self.__cookies["cobbler_xmlrpc_token"] = token
- # self.__cookies["cobbler_xmlrpc_token"]['expires'] = time.time() + 29*60
- return self.cookies
+ self.__setcookie( token, COOKIE_TIMEOUT )
+ return self.__cookies
def __get_cookie_token(self):
if self.__cookies.has_key("cobbler_xmlrpc_token"):
@@ -182,7 +235,7 @@ class CobblerWeb(object):
self.password = password
if not self.__xmlrpc_setup(is_login=True):
- return self.login(message="")
+ return self.login(message="XMLRPC Login Failed.")
return self.index()
@@ -201,7 +254,7 @@ class CobblerWeb(object):
def settings_view(self):
if not self.__xmlrpc_setup():
- return self.login(message="")
+ return self.login(message=INVALID_CREDS)
return self.__render( 'item.tmpl', {
'item_data': self.remote.get_settings(),
@@ -214,7 +267,7 @@ class CobblerWeb(object):
def distro_list(self):
if not self.__xmlrpc_setup():
- return self.login(message="")
+ return self.login(message=INVALID_CREDS)
distros = self.remote.get_distros()
if len(distros) > 0:
return self.__render( 'distro_list.tmpl', {
@@ -226,7 +279,7 @@ class CobblerWeb(object):
def distro_edit(self, name=None):
if not self.__xmlrpc_setup():
- return self.login(message="")
+ return self.login(message=INVALID_CREDS)
input_distro = None
if name is not None:
@@ -243,7 +296,7 @@ class CobblerWeb(object):
delete1=None,delete2=None,**args):
if not self.__xmlrpc_setup():
- return self.login(message="")
+ return self.login(message=INVALID_CREDS)
# handle deletes as a special case
if new_or_edit == 'edit' and delete1 and delete2:
@@ -310,7 +363,7 @@ class CobblerWeb(object):
def system_list(self):
if not self.__xmlrpc_setup():
- return self.login(message="")
+ return self.login(message=INVALID_CREDS)
systems = self.remote.get_systems()
if len(systems) > 0:
@@ -327,7 +380,7 @@ class CobblerWeb(object):
delete1=None, delete2=None, **args):
if not self.__xmlrpc_setup():
- return self.login(message="")
+ return self.login(message=INVALID_CREDS)
# parameter checking
if name is None and editmode=='edit' and oldname is not None:
@@ -400,12 +453,14 @@ class CobblerWeb(object):
def system_edit(self, name=None):
if not self.__xmlrpc_setup():
- return self.login(message="")
+ return self.login(message=INVALID_CREDS)
input_system = None
if name is not None:
input_system = self.remote.get_system(name,True)
+ input_system['netboot_enabled'] = self.__truth(input_system['netboot_enabled'])
+
return self.__render( 'system_edit.tmpl', {
'edit' : True,
'system': input_system,
@@ -417,7 +472,7 @@ class CobblerWeb(object):
# ------------------------------------------------------------------------ #
def profile_list(self):
if not self.__xmlrpc_setup():
- return self.login(message="")
+ return self.login(message=INVALID_CREDS)
profiles = self.remote.get_profiles()
if len(profiles) > 0:
return self.__render( 'profile_list.tmpl', {
@@ -430,7 +485,7 @@ class CobblerWeb(object):
def profile_edit(self, name=None):
if not self.__xmlrpc_setup():
- return self.login(message="")
+ return self.login(message=INVALID_CREDS)
input_profile = None
if name is not None:
@@ -449,7 +504,7 @@ class CobblerWeb(object):
virtpath=None,repos=None,dhcptag=None,delete1=None,delete2=None,**args):
if not self.__xmlrpc_setup():
- return self.login(message="")
+ return self.login(message=INVALID_CREDS)
# pre-command parameter checking
if name is None and editmode=='edit' and oldname is not None:
@@ -519,7 +574,7 @@ class CobblerWeb(object):
def repo_list(self):
if not self.__xmlrpc_setup():
- return self.login(message="")
+ return self.login(message=INVALID_CREDS)
repos = self.remote.get_repos()
if len(repos) > 0:
return self.__render( 'repo_list.tmpl', {
@@ -530,24 +585,25 @@ class CobblerWeb(object):
def repo_edit(self, name=None):
if not self.__xmlrpc_setup():
- return self.login(message="")
+ return self.login(message=INVALID_CREDS)
input_repo = None
if name is not None:
input_repo = self.remote.get_repo(name, True)
+ input_repo['keep_updated'] = self.__truth(input_repo['keep_updated'])
+
return self.__render( 'repo_edit.tmpl', {
'repo': input_repo,
} )
def repo_save(self,name=None,oldname=None,new_or_edit=None,editmode="edit",
- mirror=None,keepupdated=None,localfilename=None,
- rpmlist=None,createrepoflags=None,delete1=None,delete2=None,**args):
+ mirror=None,keep_updated=None,local_filename=None,
+ rpm_list=None,createrepo_flags=None,delete1=None,delete2=None,**args):
if not self.__xmlrpc_setup():
- return self.login(message="")
+ return self.login(message=INVALID_CREDS)
# pre-command parameter checking
-
if name is None and editmode=='edit' and oldname is not None:
name = oldname
if name is None:
@@ -555,7 +611,6 @@ class CobblerWeb(object):
if (editmode == 'rename' or editmode == 'copy') and name == oldname:
return self.error_page("The name has not been changed.")
-
# handle deletes as a special case
if new_or_edit == 'edit' and delete1 and delete2:
try:
@@ -580,14 +635,16 @@ class CobblerWeb(object):
try:
self.remote.modify_repo(repo, 'name', name, self.token)
self.remote.modify_repo(repo, 'mirror', mirror, self.token)
- if keepupdated:
- self.remote.modify_repo(repo, 'keep-updated', keepupdated, self.token)
- if localfilename:
- self.remote.modify_repo(repo, 'local-filename', localfilename, self.token)
- if rpmlist:
- self.remote.modify_repo(repo, 'rpm-list', rpmlist, self.token)
- if createrepoflags:
- self.remote.modify_distro(repo, 'createrepo-flags', createrepoflags, self.token)
+
+ keep_updated = self.__truth( keep_updated )
+ self.remote.modify_repo(repo, 'keep-updated', keep_updated, self.token)
+
+ if local_filename:
+ self.remote.modify_repo(repo, 'local-filename', local_filename, self.token)
+ if rpm_list:
+ self.remote.modify_repo(repo, 'rpm-list', rpm_list, self.token)
+ if createrepo_flags:
+ self.remote.modify_distro(repo, 'createrepo-flags', createrepo_flags, self.token)
self.remote.save_repo(repo, self.token)
except Exception, e:
log_exc()
@@ -599,39 +656,35 @@ class CobblerWeb(object):
except Exception, e:
return self.error_page("Rename unsuccessful. Object %s was copied instead, and the old copy (%s) still remains. Reason: %s" % (name, oldname, str(e)))
-
return self.repo_list()
-
-
# ------------------------------------------------------------------------ #
# Kickstart files
# ------------------------------------------------------------------------ #
def ksfile_list(self):
if not self.__xmlrpc_setup():
- return self.login(message="")
+ return self.login(message=INVALID_CREDS)
return self.__render( 'ksfile_list.tmpl', {
'ksfiles': self.remote.get_kickstart_templates(self.token)
} )
def ksfile_edit(self, name=None):
if not self.__xmlrpc_setup():
- return self.login(message="")
+ return self.login(message=INVALID_CREDS)
return self.__render( 'ksfile_edit.tmpl', {
- 'ksfile': name,
- 'ksdata': self.remote.read_or_write_kickstart_template(self,name,True,"",self.token)
-
+ 'name': name,
+ 'ksdata': self.remote.read_or_write_kickstart_template(name,True,"",self.token)
} )
- def ksfile_save(self, name=None, data=None):
+ def ksfile_save(self, name=None, ksdata=None, **args):
if not self.__xmlrpc_setup():
- return self.login(message="")
+ return self.login(message=INVALID_CREDS)
try:
- self.remote.read_or_write_kickstart_template(self,name,False,data,self.token)
+ self.remote.read_or_write_kickstart_template(name,False,ksdata,self.token)
except Exception, e:
- return self.error_page("error with kickstart: %s" % str(e))
- return self.ksfile_edit(name=ksfile)
+ return self.error_page("An error occurred while trying to save kickstart file %s:<br/><br/>%s" % (name,str(e)))
+ return self.ksfile_edit(name=name)
# ------------------------------------------------------------------------ #
# Miscellaneous
@@ -639,7 +692,7 @@ class CobblerWeb(object):
def sync(self):
if not self.__xmlrpc_setup():
- return self.login(message="")
+ return self.login(message=INVALID_CREDS)
try:
rc = self.remote.sync(self.token)
@@ -654,6 +707,12 @@ class CobblerWeb(object):
'message2' : "Cobbler config has been applied to filesystem."
})
+ def random_mac(self):
+ if not self.__xmlrpc_setup():
+ return self.login(message=INVALID_CREDS)
+ mac = self.remote.get_random_mac()
+ return mac
+
def error_page(self, message):
return self.__render( 'error_page.tmpl', {
'message': message
@@ -694,6 +753,7 @@ class CobblerWeb(object):
ksfile_list.exposed = True
sync.exposed = True
+ random_mac.exposed = True
class CobblerWebAuthException(exceptions.Exception):
pass
diff --git a/docs/cobbler.pod b/docs/cobbler.pod
index d32dc89..8326987 100644
--- a/docs/cobbler.pod
+++ b/docs/cobbler.pod
@@ -247,7 +247,7 @@ on your network will result in faster, more up-to-date installations and faster
are only provisioning a home setup, this will probably be overkill, though it can be very useful
for larger setups (labs, datacenters, etc).
-B<cobbler repo add --mirror=url --name=string [--local-filename=string] [--rpmlist=list] [--creatrepo-flags=string]>
+B<cobbler repo add --mirror=url --name=string [--local-filename=string] [--rpmlist=list] [--creatrepo-flags=string] [--keep-updated=Y/N]>
=over
@@ -320,6 +320,10 @@ given repository. The defaults are '-c cache'.
Specifies what architecture the repository should use. By default the current system arch (of the server) is used, which may not be desirable. Using this to override the default arch allows mirroring of source repositories (using --arch=src).
+=item keep-updated
+
+Specifies that the named repository should not be updated during a normal "cobbler reposync". The repo may still be updated by name. See "cobbler reposync" below.
+
=back
=head2 DISPLAYING CONFIGURATION ENTRIES
diff --git a/webui_content/cobblerweb.css b/webui_content/cobblerweb.css
index 806725b..c19ffc2 100644
--- a/webui_content/cobblerweb.css
+++ b/webui_content/cobblerweb.css
@@ -1,4 +1,3 @@
-
fieldset#cform label {
display: block;
width: 150px;
@@ -7,6 +6,15 @@ fieldset#cform label {
padding-right: 4em;
}
+fieldset#cform {
+ width: 98%;
+}
+
+textarea#ksdata {
+ width: 100%;
+ height: 100%;
+}
+
pre.config_data {
font-family: monospace;
background: white;
diff --git a/webui_templates/ksfile_edit.tmpl b/webui_templates/ksfile_edit.tmpl
index a28fe74..8b0eeec 100644
--- a/webui_templates/ksfile_edit.tmpl
+++ b/webui_templates/ksfile_edit.tmpl
@@ -3,11 +3,11 @@
#block body
<form method="post" action="$base_url/ksfile_save">
- <input type="hidden" name="ksfile" value="$ksfile"/>
+ <input type="hidden" name="name" value="$name"/>
<fieldset id="cform">
<legend>Edit Kickstart File</legend>
- <pre><textarea rows="40" cols="120" name="ksdata">$ksdata</textarea></pre>
+ <pre><textarea rows="40" cols="120" name="ksdata" id="ksdata">$ksdata</textarea></pre>
<br/>
<input type="submit" name="submit" value="Save"/>
diff --git a/webui_templates/login.tmpl b/webui_templates/login.tmpl
index bd3cf39..891dc4c 100644
--- a/webui_templates/login.tmpl
+++ b/webui_templates/login.tmpl
@@ -7,7 +7,7 @@
<h1>$message</h1>
#end if
-<fieldset id="login">
+<fieldset id="cform">
<legend>Log In</legend>
<label for="username">Username:</label>
diff --git a/webui_templates/repo_edit.tmpl b/webui_templates/repo_edit.tmpl
index b0907fd..100997d 100644
--- a/webui_templates/repo_edit.tmpl
+++ b/webui_templates/repo_edit.tmpl
@@ -76,18 +76,12 @@ function disablename(value)
<tr>
<td>
- <label for="keepupdated">Keep Updated</label>
+ <label for="keep_updated">Keep Updated</label>
</td>
<td>
- <input type="checkbox" name="keepupdated" id="keepupdated"
- #if $repo
- #if $repo.keep_updated
- selected="True"
- #else
- selected="False"
- #end if
- #else
- selected = "True"
+ <input type="checkbox" name="keep_updated" id="keep_updated"
+ #if not $repo or $repo.keep_updated is True
+ checked="true"
#end if
/>
<p class="context-tip">Disable to prevent the mirror from being updated.</p>
@@ -115,10 +109,10 @@ function disablename(value)
<tr>
<td>
- <label for="rpmlist">RPM List</label>
+ <label for="rpm_list">RPM List</label>
</td>
<td>
- <input type="text" size="512" style="width: 150px;" name="rpmlist" id="rpmlist"
+ <input type="text" size="512" style="width: 150px;" name="rpm_list" id="rpm_list"
#if $repo
value="$repo.rpm_list"
#end if
diff --git a/webui_templates/system_edit.tmpl b/webui_templates/system_edit.tmpl
index 133b579..f57cf98 100644
--- a/webui_templates/system_edit.tmpl
+++ b/webui_templates/system_edit.tmpl
@@ -3,8 +3,8 @@
#block body
-#if $system
<script language="javascript">
+#if $system
function disablename(value)
{
document.getElementById("name").disabled=value;
@@ -12,8 +12,21 @@ function disablename(value)
document.getElementById("name").value = "$system.name";
}
}
-</script>
+#else
+function get_random_mac()
+{
+ xmlHttp = new XMLHttpRequest();
+ xmlHttp.open("GET", "$base_url/random_mac", true);
+ xmlHttp.onreadystatechange = function () {
+ if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
+ var mac_field = document.getElementById("mac")
+ mac_field.value = xmlHttp.responseText;
+ }
+ };
+ xmlHttp.send(null);
+}
#end if
+</script>
<form method="post" action="$base_url/system_save">
<fieldset id="cform">
@@ -89,6 +102,7 @@ function disablename(value)
value="$system.mac_address"
#end if
/>
+ <a href="javascript: get_random_mac()" style="font-size: 0.8em;">random</a>
<p class="context-tip">Example: AA:BB:CC:DD:EE:FF</p>
</td>
</tr>
@@ -152,13 +166,13 @@ function disablename(value)
<tr>
<td>
<label for="netboot">Netboot Enabled</label>
- #if $system
</td>
<td>
- <input type="checkbox" checked="$system.netboot_enabled" name="netboot" id="netboot">
- #else
- <input type="checkbox" checked="True" name="netboot" id="netboot">
+ <input type="checkbox" name="netboot" id="netboot"
+ #if not $system or $system.netboot_enabled is True
+ checked="True"
#end if
+ >
<p class="context-tip">Deselect to keep this system from PXE booting</p>
</td>
</tr>
@@ -201,4 +215,5 @@ function disablename(value)
</fieldset>
</form>
+
#end block body