Commit e833c576 authored by Tomek Mrugalski's avatar Tomek Mrugalski 🛰

[5422] Initial kea-docgen implemented

parent 6214dfbc
......@@ -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], [
......
......@@ -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)
......
......@@ -92,6 +92,8 @@
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="netconf.xml"/>
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="api.xml"/>
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="faq.xml"/>
<chapter xml:id="acknowledgments">
......
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
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <map>
#include <exceptions/exceptions.h>
#include <cc/data.h>
using namespace std;
using namespace isc;
using namespace isc::data;
class DocGen {
public:
const string OUTPUT = "../guide/api.xml";
void loadFiles(const vector<string>& files) {
map <string, ElementPtr> 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 << " - Copyright (C) 2018 Internet Systems Consortium, Inc. (\"ISC\")" << endl;
f << " -" << endl;
f << " - This Source Code Form is subject to the terms of the Mozilla Public" << endl;
f << " - License, v. 2.0. If a copy of the MPL was not distributed with this" << endl;
f << " - file, You can obtain one at http://mozilla.org/MPL/2.0/." << endl;
f << " -->" << endl;
f << endl;
f << "<!-- autogenerated using cmd_docgen. Do not edit by hand! -->" << endl;
}
void generateOutput() {
stringstream f;
generateCopyright(f);
f << "<chapter xmlns=\"http://docbook.org/ns/docbook\" version=\"5.0\" xml:id=\"api\">"
<< endl;
f << " <title>Management API Reference</title>" << endl;
// Generate initial list of commands
f << " <para>Kea currently supports " << cmds_.size() << " commands:" << endl
<< " <orderedlist>" << endl;
for (auto cmd : cmds_) {
f << " <listitem><simpara>" << cmd.first << "</simpara></listitem>" << endl;
}
f << " </orderedlist>" << endl;
f << " </para>" << endl;
// Generate actual commands references.
generateCommands(f);
f << "</chapter>" << 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 << "<!-- start of " << cmd.first << " -->" << endl;
f << "<section xml:id=\"reference-" << cmd.first << "\">" << endl;
f << "<title>" << cmd.first << " reference</title>" << endl;
generateCommand(f, cmd.second);
f << "</section>" << endl;
f << "<!-- end of " << cmd.first << " -->" << 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, "<", "&lt;");
replaceAll(txt, ">", "&gt;");
return (txt);
}
void generateCommand(stringstream& f, const ElementPtr& cmd) {
// command overview
f << "<para><command>" << cmd->get("name")->stringValue() << "</command> - "
<< cmd->get("brief")->stringValue() << "</para>" << endl << endl;
// command can be issued to the following daemons
f << "<para>Supported by: ";
ConstElementPtr daemons = cmd->get("support");
for (int i = 0; i < daemons->size(); i++) {
if (i) {
f << ", ";
}
f << daemons->get(i)->stringValue();
}
f << "</para>" << endl << endl;
// availability
f << "<para>Availability: " << cmd->get("avail")->stringValue() << "</para>"
<< endl << endl;
// description and examples
f << "<para>Description and examples: See <xref linkend=\"cmd-"
<< cmd->get("name")->stringValue() << "\"/></para>" << endl << endl;
// Command syntax:
f << "<para>Command syntax:" << endl
<< " <screen>" << escapeString(cmd->get("cmd-syntax")->stringValue())
<< "</screen>"
<< endl
<< cmd->get("cmd-comment")->stringValue() << "</para>" << endl << endl;
// Response syntax
f << "<para>Response syntax:" << endl
<< " <screen>" << escapeString(cmd->get("resp-syntax")->stringValue())
<< "</screen>"
<< endl
<< cmd->get("resp-comment")->stringValue() << "</para>" << endl << endl;
}
map<string, ElementPtr> cmds_;
};
int main(int argc, const char*argv[]) {
vector<string> 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);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment