From: Tomek Mrugalski Date: Tue, 12 Jun 2018 21:32:35 +0000 (+0200) Subject: [5422] Initial kea-docgen implemented X-Git-Tag: 176-update-to-sysrepo-0-7-6-release_base~186 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e833c57668496ed349cfda5540b4a4f0fa13f1e2;p=thirdparty%2Fkea.git [5422] Initial kea-docgen implemented --- diff --git a/configure.ac b/configure.ac index ba86b3e0ad..bf731bce19 100644 --- a/configure.ac +++ b/configure.ac @@ -1648,6 +1648,7 @@ AC_CONFIG_FILES([Makefile src/share/database/scripts/pgsql/upgrade_4.0_to_5.0.sh tools/Makefile tools/path_replacer.sh + tools/cmd-docgen/Makefile ]) AC_CONFIG_COMMANDS([permissions], [ diff --git a/doc/guide/Makefile.am b/doc/guide/Makefile.am index f13cca197b..ece9ed07fe 100644 --- a/doc/guide/Makefile.am +++ b/doc/guide/Makefile.am @@ -10,6 +10,7 @@ DOCBOOK += keactrl.xml dhcp4-srv.xml dhcp6-srv.xml lease-expiration.xml logging. DOCBOOK += ddns.xml hooks.xml hooks-ha.xml hooks-host-cache.xml hooks-lease-cmds.xml DOCBOOK += hooks-radius.xml hooks-stat-cmds.xml libdhcp.xml lfc.xml stats.xml DOCBOOK += ctrl-channel.xml faq.xml classify.xml shell.xml agent.xml netconf.xml +DOCBOOK += api.xml EXTRA_DIST = $(DOCBOOK) diff --git a/doc/guide/kea-guide.xml b/doc/guide/kea-guide.xml index f0e42a47ed..ef69b636cd 100644 --- a/doc/guide/kea-guide.xml +++ b/doc/guide/kea-guide.xml @@ -92,6 +92,8 @@ + + diff --git a/tools/cmd-docgen/Makefile.am b/tools/cmd-docgen/Makefile.am new file mode 100644 index 0000000000..cbf1894281 --- /dev/null +++ b/tools/cmd-docgen/Makefile.am @@ -0,0 +1,20 @@ +SUBDIRS = . + +AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib +AM_CPPFLAGS += $(BOOST_INCLUDES) + +AM_CXXFLAGS = $(KEA_CXXFLAGS) + +AM_LDFLAGS = -static + +if GENERATE_DOCS +noinst_PROGRAMS = cmd_docgen +cmd_docgen_SOURCES = cmd_docgen.cc + +# For bare distcheck +EXTRA_DIST = cmd_docgen + +cmd_docgen_LDADD = $(top_builddir)/src/lib/cc/libkea-cc.la +cmd_docgen_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la + +endif diff --git a/tools/cmd-docgen/cmd_docgen.cc b/tools/cmd-docgen/cmd_docgen.cc new file mode 100644 index 0000000000..aaa5c1a582 --- /dev/null +++ b/tools/cmd-docgen/cmd_docgen.cc @@ -0,0 +1,239 @@ +#include +#include +#include +#include +#include + +#include +#include + +using namespace std; +using namespace isc; +using namespace isc::data; + + + +class DocGen { +public: + + const string OUTPUT = "../guide/api.xml"; + + void loadFiles(const vector& files) { + + map commands; + + int cnt = 0; + + try { + for (auto f : files) { + string cmd = f.substr(0, f.find(".")); + + cout << "Loading description of command " << cmd; + ElementPtr x = Element::fromJSONFile(f, false); + cout << "."; + + sanityCheck(f, x); + cout << "."; + cmds_.insert(make_pair(cmd, x)); + cout << "loaded" << endl; + + cnt++; + } + } catch (const Unexpected& e) { + isc_throw(Unexpected, e.what() << " while processing " + << cnt + 1 << " file out of " << files.size()); + } + + cout << "Loaded " << cmds_.size() << " commands out of " << files.size() + << " file(s)" << endl; + } + + + void requireString(const ElementPtr& x, const string& name, const string& fname) { + if (!x->contains(name)) { + isc_throw(Unexpected, "Mandatory '" + name + " field missing while " + "processing file " + fname); + } + if (x->get(name)->getType() != Element::string) { + isc_throw(BadValue, "'" + name + " field is present, but is not a string" + " in file " + fname); + } + if (x->get(name)->stringValue().empty()) { + isc_throw(BadValue, "'" + name + " field is present, is a string, but is " + "empty in file " + fname); + } + } + + void requireList(const ElementPtr& x, const string& name, const string& fname) { + if (!x->contains(name)) { + isc_throw(Unexpected, "Mandatory '" + name + " field missing while " + "processing file " + fname); + } + if (x->get(name)->getType() != Element::list) { + isc_throw(BadValue, "'" + name + " field is present, but is not a list " + "in file " + fname); + } + + ConstElementPtr l = x->get(name); + + if (l->size() == 0) { + isc_throw(BadValue, "'" + name + " field is a list, but is empty in file " + + fname); + } + + // todo: check that every element is a string + } + + void sanityCheck(const string& fname, const ElementPtr& x) { + requireString(x, "name", fname); + requireString(x, "brief", fname); + requireList (x, "support", fname); + requireString(x, "avail", fname); + requireString(x, "brief", fname); + requireString(x, "cmd-syntax", fname); + requireString(x, "cmd-comment", fname); + requireString(x, "resp-comment", fname); + requireString(x, "resp-syntax", fname); + } + + void generateCopyright(stringstream& f) { + f << "" << endl; + f << endl; + f << "" << endl; + } + + void generateOutput() { + + stringstream f; + + generateCopyright(f); + + f << "" + << endl; + f << " Management API Reference" << endl; + + // Generate initial list of commands + f << " Kea currently supports " << cmds_.size() << " commands:" << endl + << " " << endl; + + for (auto cmd : cmds_) { + f << " " << cmd.first << "" << endl; + } + + f << " " << endl; + f << " " << endl; + + // Generate actual commands references. + generateCommands(f); + + f << "" << endl; + + cout << "----------------" << endl; + ofstream file(OUTPUT.c_str(), ofstream::trunc); + file << f.str(); + cout << f.str(); + cout << "----------------" << endl; + } + + void generateCommands(stringstream& f){ + + for (auto cmd : cmds_) { + f << "" << endl; + f << "
" << endl; + f << "" << cmd.first << " reference" << endl; + generateCommand(f, cmd.second); + f << "
" << endl; + f << "" << endl; + f << endl; + } + } + +void replaceAll(std::string& str, const std::string& from, const std::string& to) { + if(from.empty()) + return; + size_t start_pos = 0; + while((start_pos = str.find(from, start_pos)) != std::string::npos) { + str.replace(start_pos, from.length(), to); + start_pos += to.length(); + } +} + +string escapeString(string txt) { + + replaceAll(txt, "<", "<"); + replaceAll(txt, ">", ">"); + return (txt); +} + +void generateCommand(stringstream& f, const ElementPtr& cmd) { + + // command overview + f << "" << cmd->get("name")->stringValue() << " - " + << cmd->get("brief")->stringValue() << "" << endl << endl; + + // command can be issued to the following daemons + f << "Supported by: "; + ConstElementPtr daemons = cmd->get("support"); + for (int i = 0; i < daemons->size(); i++) { + if (i) { + f << ", "; + } + + f << daemons->get(i)->stringValue(); + } + f << "" << endl << endl; + + // availability + f << "Availability: " << cmd->get("avail")->stringValue() << "" + << endl << endl; + + // description and examples + f << "Description and examples: See get("name")->stringValue() << "\"/>" << endl << endl; + + // Command syntax: + f << "Command syntax:" << endl + << " " << escapeString(cmd->get("cmd-syntax")->stringValue()) + << "" + << endl + << cmd->get("cmd-comment")->stringValue() << "" << endl << endl; + + // Response syntax + f << "Response syntax:" << endl + << " " << escapeString(cmd->get("resp-syntax")->stringValue()) + << "" + << endl + << cmd->get("resp-comment")->stringValue() << "" << endl << endl; +} + + map cmds_; +}; + +int main(int argc, const char*argv[]) { + + vector files; + + for (int i = 1; i < argc; i++) { + files.push_back(string(argv[i])); + } + + cout << "Loading " << files.size() << " files(s)." << endl; + + try { + DocGen doc_gen; + + doc_gen.loadFiles(files); + + doc_gen.generateOutput(); + } catch (const exception& e) { + cerr << "ERROR: " << e.what() << endl; + } + + return (0); +}