Commit 6f08c442 authored by Leigh Stoller's avatar Leigh Stoller

The Emulab Knowledge Base!

Okay, I implemented a primitive Knowledge Base! The current contents are
*all* the existing FAQ entries, which I entered manually. Here are the
details.

* My reason for doing this is that we need something very simple. The wiki
  is too much of a barrier, and its search capabilities are pathetic.

* The search page for the Knowledge Base is:

	https://www.emulab.net/kb-search.php3

  Fairly primitive keyword search. Turns out that mysql 4.0 has a bunch for
  really good text searching functions built in, but we run 3.23 ... so I
  had to roll it myself. So, its a simple keyword (space or comma
  separated) search, no regular expressions.

* Each DB record has a "faq_entry" flag, so creating the current FAQ on the
  fly from the DB is easy. See:

	https://www.emulab.net/kb-faq.php3

* In reddot mode, you can add new KB entries:

	https://www.emulab.net/kb-manage.php3

  The form is fairly obvious but here are details anyway:

    Section Name: Choose an existing title, or make up a new one.
    Title:        The title of the KB (or FAQ) entry.
    Faq Entry:    Check this box if the new entry should show up in the FAQ.
    X Ref Tag:    A token so you can refer to other KB entries by name,
                  instead of by its index. Within the KB entry you would
                  write: <a href=kb-show.php3?xref_tag=sometag>
    Body:         Whatever you like. I took the existing FAQ entries and
                  stuck them with no changes except for the xref_tag
                  mentioned about (since some entries referenced other
                  entries).

* Once you click on sumbit, you will see the entry as it will appear to
  users, along with a submenu to Modify/Delete/Add entries. You can modify
  the current entry from that menu. Mere users do not see this menu, only
  when in reddot mode.

* The intent here is that we can generate new entries really easy, right
  from email if you like (with appropriate <pre> or <xmp> tags around it).

* I have added sql/knowlbase-create.sql and a makefile target to
  generate that file when creating a distribution. I also added a section
  to install/boss-install to insert the entries into the new DB.

* I hooked the search function into the existing Search Documentation link.
  We know search both the Knowledge Base *and* the Documentation on doc
  searches. This probably needs a little more work to get right.

* I changed a lot of faq links to be more consistent and to reference
  the proper xref_tags (#swapping instead of #UTT-34).
parent 95fe00b4
......@@ -620,6 +620,18 @@ Phase "database", "Setting up database", sub {
}
ExecQuietFatal("$MYSQL $DBNAME < $TOP_SRCDIR/sql/sitevars-create.sql");
};
Phase "knowlbase", "Filling knowledge_base_entries table", sub {
my ($exitval, @rows) = ExecQuiet("echo 'select * from " .
"knowledge_base_entries' | $MYSQL -s $DBNAME");
if ($exitval) {
PhaseFail("Error running query");
}
if (scalar @rows) {
PhaseSkip("Already done");
}
ExecQuietFatal("$MYSQL $DBNAME < ".
"$TOP_SRCDIR/sql/knowlbase-create.sql");
};
};
Phase "rc.conf", "Adding testbed content to $RCCONF", sub {
......
......@@ -32,13 +32,16 @@ db-fill:
@/usr/testbed/sbin/wap /usr/testbed/sbin/export_tables \
> database-fill.sql
db-fill-dist:
db-fill-dist: knowlbase
@/usr/testbed/sbin/wap /usr/testbed/sbin/export_tables -i \
> database-fill.sql
sitevars:
@mysqldump -t tbdb sitevariables > sitevars-create.sql
knowlbase:
@mysqldump -c -t tbdb knowledge_base_entries | sed -e 's,^INSERT,REPLACE,' > knowlbase-create.sql
fwrules:
@echo "*** Use 'gmake insertrules' in the firewall subdir to do this"
......
......@@ -717,6 +717,28 @@ CREATE TABLE ipsubnets (
PRIMARY KEY (idx)
) TYPE=MyISAM;
--
-- Table structure for table `knowledge_base_entries`
--
CREATE TABLE knowledge_base_entries (
idx int(11) NOT NULL auto_increment,
creator_uid varchar(8) NOT NULL default '',
date_created datetime default NULL,
section tinytext,
title tinytext,
body text,
xref_tag tinytext,
faq_entry tinyint(1) NOT NULL default '0',
date_modified datetime default NULL,
modifier_uid varchar(8) default NULL,
archived tinyint(1) NOT NULL default '0',
date_archived datetime default NULL,
archiver_uid varchar(8) default NULL,
PRIMARY KEY (idx),
UNIQUE KEY (xref_tag)
) TYPE=MyISAM;
--
-- Table structure for table `last_reservation`
--
......
......@@ -2765,4 +2765,22 @@ last_net_act,last_cpu_act,last_ext_act);
UNIX_TIMESTAMP(exptswapout_last),
UNIX_TIMESTAMP(exptswapmod_last)));
4.5:
4.5: Add knowledge_base_entries table.
CREATE TABLE knowledge_base_entries (
idx int(11) NOT NULL auto_increment,
creator_uid varchar(8) NOT NULL default '',
date_created datetime default NULL,
section tinytext,
title tinytext,
body text,
xref_tag tinytext,
faq_entry tinyint(1) NOT NULL default '0',
date_modified datetime default NULL,
modifier_uid varchar(8) default NULL,
archived tinyint(1) NOT NULL default '0',
date_archived datetime default NULL,
archiver_uid varchar(8) default NULL,
PRIMARY KEY (idx),
UNIQUE KEY (xref_tag)
) TYPE=MyISAM;
This diff is collapsed.
<!--
EMULAB-COPYRIGHT
Copyright (c) 2000-2003 University of Utah and the Flux Group.
Copyright (c) 2000-2003, 2005 University of Utah and the Flux Group.
All rights reserved.
-->
<center>
......@@ -66,7 +66,7 @@ reserve and configure nodes, etc.
<p>
More detailed information on this
process can be found in the
<a href="docwrapper.php3?docname=faq.html">Emulab FAQ</a>.
<a href="faq.php3">Emulab FAQ</a>.
<h3>Another way of saying the same thing</h3>
......
<?php
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2004 University of Utah and the Flux Group.
# Copyright (c) 2000-2005 University of Utah and the Flux Group.
# All rights reserved.
#
......@@ -197,7 +197,7 @@ function SPITFORM($formfields, $errors)
<li><b>If you do not have an NS file:</b><br> You may want to
<b><a href='buildui/bui.php3'>go to the NetBuild GUI</a></b>
to graphically create one online<font size=-2>
(<a href='$TBDOCBASE/faq.php3#UTT-NETBUILD'>Additional
(<a href='$TBDOCBASE/faq.php3#netbuild'>Additional
information</a>)</font>.<br>
Or, you can download the Emulab
<a href='netlab/client.php3'><b>client</b></a> and graphically
......
<?php
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2002, 2004 University of Utah and the Flux Group.
# Copyright (c) 2000-2002, 2004, 2005 University of Utah and the Flux Group.
# All rights reserved.
#
require("defs.php3");
......@@ -32,7 +32,7 @@ PAGEHEADER("Documentation");
</dl><br>
<li><dl>
<dt><b><a href="docwrapper.php3?docname=faq.html">
<dt><b><a href="faq.php3">
Frequently Asked Questions (FAQ)</a></b></dt>
<dd>Probably our most important reference document. Check here before
asking for help. Includes sections on "Getting Started" on Emulab,
......
......@@ -8,7 +8,7 @@
<body>
<!--
EMULAB-COPYRIGHT
Copyright (c) 2000-2004 University of Utah and the Flux Group.
Copyright (c) 2000-2005 University of Utah and the Flux Group.
All rights reserved.
-->
<h2>Windows XP in Emulab</h2>
......@@ -218,7 +218,7 @@ buttons work, from FreeBSD, Linux, and Windows workstations:
node in a /sshkeys subdirectory, you have to type your password to SSH
logins to enable remote directory mounts through SMB. </li>
<li> <a href="https://www.emulab.net/faq.php3#UTT-TUNNEL"> <b>Console</b>
<li> <a href="https://www.emulab.net/faq.php3#tiptunnel"> <b>Console</b>
</a> - The <b>Console</b> button won't do much good now, because
Windows doesn't normally provide logins on the com serial device.
(This will be remedied with <i>agetty</i> eventually.) An exception is
......
This diff is collapsed.
<?php
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2002 University of Utah and the Flux Group.
# Copyright (c) 2000-2005 University of Utah and the Flux Group.
# All rights reserved.
#
require("defs.php3");
#
# Standard Testbed Header
#
PAGEHEADER("Frequently Asked Questions");
echo("<b><a href=$TBDOCBASE/docwrapper.php3?docname=faq.html&printable=1>Printable version of this document</a></b><br>");
#
# I don't want to stick the html code in here.
#
readfile("faq.html");
#
# Standard Testbed Footer
#
PAGEFOOTER();
header("Location: kb-faq.php3");
?>
<?php
#
# EMULAB-COPYRIGHT
# Copyright (c) 2005 University of Utah and the Flux Group.
# All rights reserved.
#
require("defs.php3");
#
# Standard Testbed Header
#
PAGEHEADER("Emulab FAQ");
#
# Get all FAQ entries.
#
$search_result =
DBQueryFatal("select * from knowledge_base_entries ".
"where faq_entry=1 ".
"order by section,date_created");
if (! mysql_num_rows($search_result)) {
USERERROR("There are no FAQ entries in the Emulab Knowledge Base!", 1);
return;
}
echo "<center><h2>Frequently Asked Questions</h2></center>\n";
echo "<blockquote>\n";
echo "<ul>\n";
$lastsection = "";
while ($row = mysql_fetch_array($search_result)) {
$section = $row['section'];
$title = $row['title'];
$idx = $row['idx'];
$xref_tag = $row['xref_tag'];
if ($lastsection != $section) {
if ($lastsection != "") {
echo "</ul><hr>\n";
}
$lastsection = $section;
echo "<li><font size=+1><b>$section</b></font>\n";
echo "<ul>\n";
}
echo "<li>";
if (isset($xref_tag) && $xref_tag != "") {
echo "<a NAME='$xref_tag'></a>";
}
echo "<a href=kb-show.php3?idx=$idx>$title</a>\n";
}
echo "</ul>\n";
echo "</blockquote>\n";
#
# Standard Testbed Footer
#
PAGEFOOTER();
?>
This diff is collapsed.
<?php
#
# EMULAB-COPYRIGHT
# Copyright (c) 2005 University of Utah and the Flux Group.
# All rights reserved.
#
if (!isset($embedded)) {
require("defs.php3");
}
#
# Standard Testbed Header
#
if (!isset($embedded)) {
PAGEHEADER("Search Emulab Knowledge Base");
}
function SPITFORM($query, $query_type, $query_which, $error)
{
echo "<table align=center border=1>
<form action=kb-search.php3 method=get>\n";
$query = htmlspecialchars($query);
if ($error) {
echo "<center><font color=red>
$error
</font></center><br>\n";
}
#
# The query
#
echo "<tr>
<td>Keywords:</td>
<td class=left>
<input type=text name=query value=\"$query\"
size=50 maxlength=100>
</td>
</tr>\n";
#
# The query type
#
$temp_array = array("and" => "All Words",
"or" => "Any Words",
"exact" => "Exact Phrase");
if (!$query_type)
$query_type = "and";
echo "<tr>
<td>Search for:</td>
<td class=left>\n";
foreach ($temp_array as $key => $phrase) {
$checked = "";
if ($query_type == $key)
$checked = "checked";
echo "<input type=radio $checked name=query_type value=$key>$phrase\n";
}
echo " </td>
</tr>\n";
#
# What to search
#
$temp_array = array("title" => "Title",
"body" => "Body",
"both" => "Both");
if (!$query_which)
$query_which = "title";
echo "<tr>
<td>Search what:</td>
<td class=left>\n";
foreach ($temp_array as $key => $phrase) {
$checked = "";
if ($query_which == $key)
$checked = "checked";
echo "<input type=radio $checked name=query_which value=$key>".
"$phrase\n";
}
echo " </td>
</tr>\n";
echo "<tr>
<td colspan=2 align=center>
<b><input type=submit name=submit value='Submit Query'></b>
</td>
</tr>\n";
echo "</form>
</table><br>\n";
echo "<center>".
"Enter a space or comma separated list of keywords<br>".
"Use <font size=+2><b>*</b></font> to get all entries".
"</center>\n";
}
#
# First page load ...
#
if (!isset($submit) && !isset($embedded)) {
SPITFORM("", null, null, null);
PAGEFOOTER();
return;
}
#
# Check the query type
#
if (!isset($query_type) || $query_type == "") {
$query_type == "and";
}
if (! ($query_type == "and" || $query_type == "or" ||
$query_type == "exact")) {
PAGEARGERROR("Improper query type $query_type");
}
#
# Check the query which
#
if (!isset($query_which) || $query_which == "") {
$query_which == "title";
}
if (! ($query_which == "title" ||
$query_which == "body" || $query_which == "both")) {
PAGEARGERROR("Improper query which $query_which");
}
#
# Must supply a query!
#
if (!isset($query) || $query == "") {
SPITFORM("", $query_type, $query_which, "Please provide a query!");
PAGEFOOTER();
return;
}
#
# Check the query
#
if (! TBvalid_userdata($query)) {
SPITFORM($query, $query_type, $query_which, "Illegal characters in query");
PAGEFOOTER();
return;
}
#
# Look for special "*" query; just get everything and list it.
#
if ($query == "*") {
$search_result =
DBQueryFatal("select * from knowledge_base_entries ".
"order by section,date_created");
}
else {
#
# Mysql 4.0 has all this stuff built in, but not 3.23. So, do it by hand.
#
#
# Exact phrase search is easy!
#
if ($query_type == "exact") {
$clause = "";
$qsafe = addslashes($query);
if ($query_which == "title") {
$clause = "where title like '%${qsafe}%'";
}
elseif ($query_which == "body") {
$clause = "where body like '%${qsafe}%'";
}
elseif ($query_which == "both") {
$clause = "where body like '%${qsafe}%' ".
"or title like '%${qsafe}%'";
}
}
elseif ($query_type == "or") {
$wordarray = preg_split("/[\s,]+/", $query);
foreach ($wordarray as $i => $word) {
$wordarray[$i] = addslashes($word);
}
$qstring = implode("|", $wordarray);
if ($query_which == "title") {
$clause = "where title regexp '$qstring'";
}
elseif ($query_which == "body") {
$clause = "where body regexp '$qstring'";
}
elseif ($query_which == "both") {
$clause = "where title regexp '$qstring' ".
"or body regexp '$qstring'";
}
}
else {
$wordarray = preg_split("/[\s,]+/", $query);
foreach ($wordarray as $i => $word) {
if ($query_which == "title") {
$wordarray[$i] = "title regexp '" . addslashes($word) . "'";
}
elseif ($query_which == "body") {
$wordarray[$i] = "body regexp '" . addslashes($word) . "'";
}
else {
$wordarray[$i] = "(title regexp '" . addslashes($word) . "' ".
"or body regexp '" . addslashes($word) . "')";
}
}
$clause = "where ". implode(" and ", $wordarray);
}
$search_result =
DBQueryFatal("select * from knowledge_base_entries ".
"$clause ".
"order by section,date_created");
}
if (! mysql_num_rows($search_result)) {
if (!isset($embedded)) {
SPITFORM($query, $query_type, $query_which,
"No Matches. Please try again");
PAGEFOOTER();
}
return;
}
#
# Okay, format the list ...
#
if (!isset($embedded)) {
SPITFORM($query, $query_type, $query_which, null);
}
if (!isset($embedded)) {
echo "<blockquote><blockquote>\n";
}
echo "<font size=+2>Knowledge Base search results</font>\n";
echo "<ul>\n";
$lastsection = "";
while ($row = mysql_fetch_array($search_result)) {
$section = $row['section'];
$title = $row['title'];
$idx = $row['idx'];
$xref_tag = $row['xref_tag'];
if ($lastsection != $section) {
if ($lastsection != "") {
echo "</ul><hr>\n";
}
$lastsection = $section;
echo "<li><font size=+1><b>$section</b></font>\n";
echo "<ul>\n";
}
echo "<li>";
if (isset($xref_tag) && $xref_tag != "") {
echo "<a NAME='$xref_tag'></a>";
}
echo "<a href=kb-show.php3?idx=$idx>$title</a>\n";
}
echo "</ul></ul>\n";
if (!isset($embedded)) {
echo "</blockquote></blockquote>\n";
}
#
# Standard Testbed Footer
#
if (!isset($embedded)) {
PAGEFOOTER();
}
?>
<?php
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2005 University of Utah and the Flux Group.
# All rights reserved.
#
include("defs.php3");
#
# Standard Testbed Header
#
PAGEHEADER("Knowledge Base Entry");
#
# Admin users get a menu.
#
$uid = GETLOGIN();
$isadmin = 0;
if (CHECKLOGIN($uid) & CHECKLOGIN_LOGGEDIN) {
$isadmin = ISADMIN();
}
#
# Check record.
#
if (isset($idx) && $idx != "") {
if (! TBvalid_integer($idx)) {
PAGEARGERROR("Invalid characters in $idx");
}
$query_result =
DBQueryFatal("select * from knowledge_base_entries ".
"where idx='$idx'");
if (! mysql_num_rows($query_result)) {
USERERROR("No such knowledge_base entry: $idx", 1);
}
$row = mysql_fetch_array($query_result);
}
elseif (isset($xref_tag) && $xref_tag != "") {
if (! preg_match("/^[-\w]+$/", $xref_tag)) {
PAGEARGERROR("Invalid characters in $xref_tag");
}
$query_result =
DBQueryFatal("select * from knowledge_base_entries ".
"where xref_tag='$xref_tag'");
if (! mysql_num_rows($query_result)) {
USERERROR("No such knowledge_base entry: $xref_tag", 1);
}
$row = mysql_fetch_array($query_result);
$idx = $row['idx'];
}
else {
PAGEARGERROR("Must supply a knowledge_base index or xref tag");
}
echo "<center><b>Knowledge Base Entry: $idx</b><br>".
"(<a href=kb-search.php3>Search Again</a>)</center>\n";
echo "<br><br>\n";
if ($isadmin) {
SUBPAGESTART();
SUBMENUSTART("Options");
WRITESUBMENUBUTTON("Modify Entry",
"kb-manage.php3?idx=$idx&action=edit");
WRITESUBMENUBUTTON("Delete Entry",
"kb-manage.php3?idx=$idx&action=delete");
WRITESUBMENUBUTTON("Add Entry", "kb-manage.php3");
SUBMENUEND();
}
echo "<blockquote>\n";
echo "<font size=+1>" . $row['title'] . "</font>\n";
echo "<br><br>\n";
echo $row['body'];
echo "<br><br><br><br>\n";
echo "<font size=-2>Posted by " . $row['creator_uid'] . " on " .
$row['date_created'] . "</font><br><br>\n";
if ($isadmin) {
echo "</blockquote>\n";
SUBPAGEEND();
}
PAGEFOOTER();
?>
......@@ -588,6 +588,9 @@ function WRITESIDEBAR() {
WRITESIDEBARBUTTON("Edit Site Variables",
$TBBASE, "editsitevars.php3");
WRITESIDEBARBUTTON("Edit Knowledge Base",
$TBBASE, "kb-manage.php3");
$query_result
= DBQUeryFatal("select new_node_id from new_nodes");
if (mysql_num_rows($query_result) > 0) {
......
......@@ -102,7 +102,7 @@ echo "<br><br>\n";
# Put up the modify form on first load.
#
if (! isset($go)) {
echo "<a href='faq.php3#UTT-Modify'>".
echo "<a href='faq.php3#swapmod'>".
"Modify Experiment Documentation (FAQ)</a></h3>";
echo "<br>";
......
<?php
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2002, 2004 University of Utah and the Flux Group.
# Copyright (c) 2000-2002, 2004, 2005 University of Utah and the Flux Group.
# All rights reserved.
#
require("defs.php3");
......@@ -15,7 +15,7 @@ PAGEHEADER("Search Emulab Documentation");
# We no longer support an advanced search option. We might bring it back
# someday.
#
function SPITFORM($query)
function SPITSEARCHFORM($query)
{
echo "<table align=center border=1>
<form action=search.php3 method=get>\n";
......@@ -43,14 +43,14 @@ function SPITFORM($query)
}
if (!isset($query) || $query == "") {
SPITFORM("");
SPITSEARCHFORM("");
PAGEFOOTER();
return;
}
# Sanitize for the shell. Be fancy later.
if (!preg_match("/^[-\w\ \"]+$/", $query)) {
SPITFORM("");
SPITSEARCHFORM("");
PAGEFOOTER();
return;
}
......@@ -77,9 +77,20 @@ function CLEANUP()
ignore_user_abort(1);
register_shutdown_function("CLEANUP");
SPITFORM($query);
SPITSEARCHFORM($query);
flush();
#
# First the Knowledge Base
#
$embedded = 1;
$query_type = "and";
$query_which = "both";
include("kb-search.php3");
echo "<br>\n";
echo "<font size=+2>Documentation search results</font><br>\n";
if ($fp = popen("$TBSUEXEC_PATH nobody nobody websearch '$query'", "r")) {
while (!feof($fp)) {
$string = fgets($fp, 1024);
......
......@@ -106,7 +106,7 @@ SUBMENUSTART("Node Options");
#
if (TBHasSerialConsole($node_id)) {
WRITESUBMENUBUTTON("Connect to Serial Line</a> " .
"<a href=\"faq.php3#UTT-TUNNEL\">(howto)",
"<a href=\"faq.php3#tiptunnel\">(howto)",
"nodetipacl.php3?node_id=$node_id");
}
......
......@@ -1201,7 +1201,7 @@ function SHOWNODES($pid, $eid, $sortby) {
<th>Last Log Message</th>\n";
}
echo " <th><a href=\"docwrapper.php3?docname=ssh-mime.html\">SSH</a></th>
<th><a href=\"faq.php3#UTT-TUNNEL\">Console</a></th>";
<th><a href=\"faq.php3#tiptunnel\">Console</a></th>";
# Only put out a RDP column header if there are any Windows nodes.
$windows_query_result = DBQueryFatal("SELECT r.pid,r.eid,n.node_id,oi.OS ".
......
<!--
EMULAB-COPYRIGHT
Copyright (c) 2000-2002 University of Utah and the Flux Group.
Copyright (c) 2000-2002, 2005 University of Utah and the Flux Group.
All rights reserved.
-->
<center>
......@@ -65,7 +65,7 @@
Click <a href="https://www.emulab.net/showosid_list.php3">List ImageIDs and
OSIDs</a> in the Emulab web interface "Interaction" pane to see the
current list of Emulab-supplied OSs,
and see the <a href="faq.php3#SWS-2">FAQ</a> and
and see the <a href="faq.php3#tb-set-node-os">FAQ</a> and
<a href = "tutorial/tutorial.php3#OsChoices">Tutorial</a>
for more information.
......
......@@ -182,12 +182,12 @@ if (!$confirmed) {
if (!strcmp($inout, "restart")) {
echo "<p>