---
snake-server | 2 ++
snake.spec | 3 ++-
snake/config.py | 8 +++++---
snake/install.py | 5 +++--
snake/ksdb.py | 9 +++++++++
snake/machinedb.py | 43 +++++++++++++++++++++++++++++++++++++++++++
snake/tui.py | 40 ++++++++++++++++++++++++++++------------
7 files changed, 92 insertions(+), 18 deletions(-)
diff --git a/snake-server b/snake-server
index 4a1bb93..cc7b8db 100755
--- a/snake-server
+++ b/snake-server
@@ -202,6 +202,8 @@ if __name__ == "__main__":
server.register_function(snake.machinedb.remove,'machine.remove')
server.register_function(snake.machinedb.add,'machine.add')
server.register_function(snake.machinedb.update,'machine.update')
+ server.register_function(snake.config.getoption,'server.getoption')
+ server.register_function(snake.machinedb.getksurl,'machine.getksurl')
# FIXME: add check function (like snake-tree)
server.register_function(snake.ksdb.listkickstarts,'kickstart.list')
server.register_function(snake.ksdb.write_data,'kickstart.add')
diff --git a/snake.spec b/snake.spec
index ddb8efa..4abcedf 100644
--- a/snake.spec
+++ b/snake.spec
@@ -135,7 +135,8 @@ fi
%changelog
-* Tue Jun 02 2008 James Laska <jlaska(a)redhat.com> 0.11-0.6
+* Tue Jun 13 2008 James Laska <jlaska(a)redhat.com> 0.11-0.6
+- ticket#37 - snake-server support for hosting http,ftp and nfs kickstarts
- ticket#42 - snake/tui.py - add ksmethod selection screen
- snake-install-tui should remember the selected tree
- ticket#53 - Add snake-ks --ksmeta parameter to pass optional values to the
diff --git a/snake/config.py b/snake/config.py
index aaccd0e..2c5055b 100644
--- a/snake/config.py
+++ b/snake/config.py
@@ -31,7 +31,7 @@ def getipaddr():
DEFAULT_CONFIGFILE = "/etc/snake.conf"
DEFAULT_HOSTNAME = gethostname()
DEFAULT_IPADDR = getipaddr()
-DEFAULT_PORT = 2903 # used by "suitcase".. but do we care?
+DEFAULT_PORT = 2903 # used by "suitcase".. but do we care?
# Default configs
defaults={
@@ -41,8 +41,10 @@ defaults={
"port": DEFAULT_PORT,
"zeroconf": "yes",
"sname": "SNAKE",
- "http_dir": "/var/www/html/snake",
- "http_root": "",
+ "ftp_root": "/", # root
directory used by ftp clients
+ "http_root": "/", # root
directory used by http clients
+ "nfs_dir": "/var/lib/snake/machines", #
directory used by nfs clients
+ "ksmethods": "http ftp nfs", #
Supported schemas for accessing kickstarts
"xmlcachedir": "/var/cache/snake",
"plugindir": "/var/lib/snake/plugins",
"templatedir": "/var/lib/snake/templates",
diff --git a/snake/install.py b/snake/install.py
index 6637a00..365fb82 100644
--- a/snake/install.py
+++ b/snake/install.py
@@ -211,9 +211,10 @@ def fetch_and_prep(uri,**kwargs):
# where to put the kickstart file?
ksmethod = kwargs.get("ksmethod","initrd")
- # On remote volume - TODO
+ # On remote volume
if ksmethod in ("http","ftp","nfs"):
- raise ValueError(_("%s Kickstart delivery not yet supported" %
ksmethod))
+ '''no action required. The ks url should already be on the
+ boot args'''
# On local disk - defaults to /boot partition
elif ksmethod.startswith("hd"):
diff --git a/snake/ksdb.py b/snake/ksdb.py
index 633177d..a4488a1 100644
--- a/snake/ksdb.py
+++ b/snake/ksdb.py
@@ -186,6 +186,15 @@ def generate(name,kwargs={}):
if matches[0].ksdata:
for l in matches[0].ksdata.split("\n"):
k.handlecommand(l)
+
+ # save the machine-specific kickstart on the server
+ try:
+ cacheksfile = os.path.join(getoption("machinedb_dir"),
"%s" % matches[0].id, "ks.cfg")
+ fd = open(cacheksfile, "w")
+ fd.write(str(k))
+ fd.close()
+ except Exception, e:
+ log.exception("Error while saving generated kickstart to
'%s'" % cacheksfile)
elif len(matches) > 1:
log.debug("Kickstart generate: found %s machines with fingerprints:
%s" % (len(matches), kwargs.get("fingerprints")))
diff --git a/snake/machinedb.py b/snake/machinedb.py
index 3ea9f9e..683d61c 100644
--- a/snake/machinedb.py
+++ b/snake/machinedb.py
@@ -210,3 +210,46 @@ def remove(id):
return True # XMLRPC methods must return a value
else:
return False # XMLRPC methods must return a value
+
+def getksurl(hwinfo, scheme):
+ ''' Return a URL to a machine-specific kickstart file location
+ Accepted arguments:
+ hwinfo (dict) - Dictionary containing identifying information for this
+ system (id, fingerprints, nickname)
+ scheme (string) - A string representing the requested access method
+ (options include http, ftp, and nfs)
+ '''
+
+ # Find a machine with matching information
+ id = None
+ if hwinfo.has_key("id"):
+ id = hwinfo.get("id")
+ else:
+ matches = list()
+ if hwinfo.has_key("nickname"):
+ matches = listmachines(nickname=hwinfo["nickname"])
+ elif hwinfo.has_key("fingerprints"):
+ matches = listmachines(fingerprint=hwinfo["fingerprints"])
+
+ if len(matches) == 1:
+ id = matches[0].id
+ else:
+ raise ValueError("Multiple machines found matching supplied criteria
'%s'" % hwinfo)
+
+ ksurl = ""
+ # Was a valid machine registration found?
+ if id is None:
+ logging.warn("No machine matching supplied hwinfo found")
+ else:
+ # Generate a remotely accessible URL
+ if scheme == "http":
+ ksurl = "http://%s/%s" % (getoption("hostname"),
os.path.join(getoption("http_root"), str(id), "ks.cfg"))
+ elif scheme == "ftp":
+ ksurl = "ftp://%s/%s" % (getoption("hostname"),
os.path.join(getoption("ftp_root"), str(id), "ks.cfg"))
+ elif scheme == "nfs":
+ ksurl = "nfs:%s:/%s" % (getoption("hostname"),
os.path.join(getoption("nfs_dir"), str(id), "ks.cfg"))
+ else:
+ logging.error("Unknown kickstart method requested '%s'" %
scheme)
+
+ logging.debug("Returning hosted kickstart URL - '%s'" % ksurl)
+ return ksurl
diff --git a/snake/tui.py b/snake/tui.py
index 491df83..e0490b8 100644
--- a/snake/tui.py
+++ b/snake/tui.py
@@ -223,6 +223,8 @@ class FindServerWindow(SnakeWindow):
# Connect and save connection object
self.tui.xmlrpc = snake.client.connect(self.cfg.server, self.cfg.port)
+ # Save list of supported methods for future calls
+ self.tui.supported_methods = self.tui.xmlrpc.system.listMethods()
# Update screen text with connection info
self.screen.drawRootText(0, 0, self.tui.title + " [%s:%s]" %
(self.cfg.server, self.cfg.port))
@@ -311,10 +313,14 @@ class KSMethodWindow(SnakeWindow):
SnakeWindow.__init__(self, *kwargs)
def __call__(self):
- # TODO - probe server for supported methods
items = [(_("initrd - rebuilds the boot initrd to include a /ks.cfg
file"),"initrd"),
(_("hd - places the ks.cfg file in the /boot partition"),
"hd")]
+ # probe the server for a list of supported methods
+ if "server.getoption" in self.tui.supported_methods:
+ for ksmethod in
self.tui.xmlrpc.server.getoption("ksmethods").split():
+ items.append((_("%s - hosted by %s" % (ksmethod,
self.cfg.server)), ksmethod))
+
(button, self.cfg.ksmethod) = snack.ListboxChoiceWindow(self.screen,
"Kickstart Delivery", "Choose a kickstart delivery method from
the list below.",
items, width=self.tui.width, height=min(self.tui.height,len(items)),
@@ -404,16 +410,15 @@ class BootLoaderAppendWindow(SnakeWindow):
def __call__(self):
- supported_methods = self.tui.xmlrpc.system.listMethods()
- fingerprints = snake.machineinfo.get_fingerprints()
+ self.cfg.fingerprints = snake.machineinfo.get_fingerprints()
# is the server suggesting any boot arguments?
- if "kernel_args.generate" in supported_methods:
+ if "kernel_args.generate" in self.tui.supported_methods:
argdata = dict()
argdata['cmdline'] =
getattr(self.cfg,"cmdline","")
argdata['tree'] = self.cfg.tree.id
argdata['uri'] = self.cfg.uri
- argdata['fingerprints'] = fingerprints
+ argdata['fingerprints'] = self.cfg.fingerprints
if self.cfg.ks:
argdata['ks'] = self.cfg.ks
@@ -439,7 +444,7 @@ class BootLoaderAppendWindow(SnakeWindow):
# if the server supports saving machine specific kernel args ...
cb = snack.Checkbox(_("Always use these options for this system?"))
- if "machine.update" not in supported_methods:
+ if "machine.update" not in self.tui.supported_methods:
cb.setFlags(snack.FLAG_DISABLED, snack.FLAGS_SET)
entry = snack.Entry(self.tui.width, scroll=1, returnExit=1,
text=self.cfg.kernel_args.strip())
@@ -463,12 +468,12 @@ class BootLoaderAppendWindow(SnakeWindow):
# asked to remember these values?
if cb.selected() \
- and "machine.update" in supported_methods \
- and "machine.add" in supported_methods \
- and "machine.list" in supported_methods:
+ and "machine.update" in self.tui.supported_methods \
+ and "machine.add" in self.tui.supported_methods \
+ and "machine.list" in self.tui.supported_methods:
# Determine if a matching system registration exists?
- machines = self.tui.xmlrpc.machine.list({"fingerprints":
fingerprints})
+ machines = self.tui.xmlrpc.machine.list({"fingerprints":
self.cfg.fingerprints})
if len(machines) == 1:
m = machines.pop()
self.tui.xmlrpc.machine.update(m.get("id"),
{"bootargs": self.cfg.kernel_args})
@@ -477,12 +482,12 @@ class BootLoaderAppendWindow(SnakeWindow):
elif len(machines) == 0:
self.tui.xmlrpc.machine.add(gethostname(), \
{"bootargs": self.cfg.kernel_args, \
- "fingerprints": fingerprints, \
+ "fingerprints": self.cfg.fingerprints, \
"arch": rpmUtils.arch.getBaseArch()})
# FIXME ... should we quietly fail here and let the snake-server admin
examine the logs?
else:
- raise Exception("Unable to save boot arguments - multiple matches
for supplied fingerprints %s" % fingerprints)
+ raise Exception("Unable to save boot arguments - multiple matches
for supplied fingerprints %s" % self.cfg.fingerprints)
return button
@@ -550,12 +555,23 @@ which will be installed from the following location:
fpargs["ksdata"] =
self.tui.xmlrpc.kickstart.generate(self.cfg.ks, \
{"commands": [method], \
"version": ver, \
+ "fingerprints": self.cfg.fingerprints, \
"ksmeta":
dict(arch=fpargs.get("arch"))})
# was a kickstart method of delivery specified?
if self.cfg.ksmethod:
fpargs["ksmethod"] = self.cfg.ksmethod
+ # if a network ksmethod ... ask the server for a valid URL
+ if self.cfg.ksmethod in ["http", "ftp",
"nfs"]:
+ if "machine.getksurl" in self.tui.supported_methods:
+ self.cfg.kernel_args += " ks=%s" %
self.tui.xmlrpc.machine.getksurl({"fingerprints": self.cfg.fingerprints},
self.cfg.ksmethod)
+ else:
+ error_message = _("Kickstart access method '%s'
is not supported on this server." % ksmethod)
+ r = snack.ButtonChoiceWindow(self.screen,"Unsupported
kickstart access method", error_message,
+ exit_button, def_width)
+ return (r, "Cancelled.")
+
# setup progress callback
fpargs["progress_callback"] = progressWindow(self.screen,
self.tui.width)
--
1.5.5.1