Revision 4352bf6d

b/.gitignore
36 36
/doc/*.in
37 37
/doc/*.pdf
38 38
/doc/*.png
39
/doc/rapi-resources.sgml
39
/doc/rapi-resources.gen
40 40

  
41 41
# doc/examples
42 42
/doc/examples/bash_completion
b/Makefile.am
44 44
	doc/*.in \
45 45
	doc/*.pdf \
46 46
	$(patsubst %.dot,%.png,$(docdot)) \
47
	doc/rapi-resources.sgml \
47
	doc/rapi-resources.gen \
48 48
	doc/examples/bash_completion \
49 49
	doc/examples/ganeti.initd \
50 50
	doc/examples/ganeti.cron \
......
108 108
	lib/http/server.py
109 109

  
110 110

  
111
docsgml = \
112
	doc/rapi.sgml
113

  
114 111
docrst = \
115 112
	doc/admin.rst \
116 113
	doc/design-2.0.rst \
......
123 120
	doc/arch-2.0.dot
124 121

  
125 122
doc_DATA = \
126
	$(patsubst %.rst,%.html,$(docrst)) \
127
	$(patsubst %.sgml,%.html,$(docsgml)) \
128
	$(patsubst %.sgml,%.pdf,$(docsgml))
123
	$(patsubst %.rst,%.html,$(docrst))
129 124

  
130 125
noinst_DATA = $(manhtml)
131 126

  
......
155 150
	devel/upload.in \
156 151
	$(docrst) \
157 152
	$(docdot) \
158
	$(docsgml) \
159 153
	doc/build-rapi-resources-doc \
160 154
	doc/examples/bash_completion.in \
161 155
	doc/examples/ganeti.initd.in \
......
228 222

  
229 223
TESTS_ENVIRONMENT = PYTHONPATH=.:$(top_builddir)
230 224

  
225
RAPI_RESOURCES = $(wildcard lib/rapi/*.py)
231 226

  
232 227
all-local: stamp-directories lib/_autoconf.py devel/upload \
233 228
	doc/examples/bash_completion \
......
250 245
doc/%.pdf: doc/%.in $(DOCBOOK_WRAPPER)
251 246
	$(DOCBOOK_WRAPPER) "$(DOCBOOK2PDF)" $< $@
252 247

  
253
doc/%.html: doc/%.in $(DOCBOOK_WRAPPER)
254
	$(DOCBOOK_WRAPPER) "$(DOCBOOK2HTML) --nochunks" $< $@
255

  
256 248
doc/%.html: doc/%.rst
257 249
	$(RST2HTML) $< $@
258 250

  
......
261 253

  
262 254
doc/design-2.0.html: doc/design-2.0.rst doc/arch-2.0.png
263 255

  
264
doc/rapi.pdf doc/rapi.html doc/rapi.in: doc/rapi-resources.sgml
256
doc/rapi.pdf doc/rapi.html: doc/rapi-resources.gen
265 257

  
266
doc/rapi-resources.sgml: $(BUILD_RAPI_RESOURCE_DOC) lib/rapi/connector.py
267
	PYTHONPATH=.:$(top_builddir) $(BUILD_RAPI_RESOURCE_DOC) > $@ || rm -f $@
258
doc/rapi-resources.gen: $(BUILD_RAPI_RESOURCE_DOC) $(RAPI_RESOURCES)
259
	PYTHONPATH=.:$(top_builddir) $(BUILD_RAPI_RESOURCE_DOC) > $@ || \
260
	  rm -f $@
268 261

  
269 262
man/%.7: man/%.in man/footer.sgml $(DOCBOOK_WRAPPER)
270 263
	$(DOCBOOK_WRAPPER) "$(DOCBOOK2MAN)" $< $@
......
322 315
	  echo 's#@CUSTOM_XEN_KERNEL@#$(XEN_KERNEL)#g'; \
323 316
	  echo 's#@CUSTOM_XEN_INITRD@#$(XEN_INITRD)#g'; \
324 317
	  echo 's#@RPL_FILE_STORAGE_DIR@#$(FILE_STORAGE_DIR)#g'; \
325
	  echo '/@INCLUDE_RAPI_RESOURCES@/ {'; \
326
	  echo '  r $(abs_top_builddir)/doc/rapi-resources.sgml'; \
327
	  echo '  d'; \
328
	  echo '}'; \
329 318
	} > $@
330 319

  
331 320
# We need to create symlinks because "make distcheck" will not install Python
b/doc/build-rapi-resources-doc
20 20

  
21 21
"""Script to generate documentation for remote API resources.
22 22

  
23
This is hard-coded to the section numbering we have in the master RST
24
document.
25

  
23 26
"""
24 27

  
25 28
import re
......
33 36
CHECKED_COMMANDS = ["GET", "POST", "PUT", "DELETE"]
34 37

  
35 38

  
39
def beautify(text):
40
  """A couple of small enhancements, epydoc-to-rst.
41

  
42
  """
43
  pairs = [
44
    ("@return:", "Returns:"),
45
    ]
46

  
47
  for old, new in pairs:
48
    text = text.replace(old, new)
49

  
50
  return text
51

  
52

  
53
def indent(text):
54
  """Returns a text block with all lines indented.
55

  
56
  """
57
  lines = text.splitlines()
58
  lines = ["  " + l for l in lines]
59
  return "\n".join(lines)
60

  
61

  
36 62
def main():
37 63
  # Get list of all resources
38 64
  all = list(connector.CONNECTOR.itervalues())
......
40 66
  # Sort rlib by URI
41 67
  all.sort(cmp=lambda a, b: cmp(a.DOC_URI, b.DOC_URI))
42 68

  
43
  print "<!-- Automatically generated, do not edit -->"
69
  print ".. Automatically generated, do not edit\n"
44 70

  
45 71
  for cls in all:
46
    print "<sect2>"
47
    print "<title>%s</title>" % cgi.escape(cls.DOC_URI)
72
    title = cls.DOC_URI
73
    print "%s\n%s\n" % (title, "+" * len(title))
48 74

  
49 75
    # Class docstring
50 76
    description = inspect.getdoc(cls)
51 77
    if description:
52
      print ("<literallayout>%s</literallayout>" %
53
             cgi.escape(description.strip()))
54

  
55
    print '<informaltable><tgroup cols="2">'
56
    print '<colspec colwidth="1*">'
57
    print '<colspec colwidth="5*">'
58
    print "<thead>"
59
    print "  <row>"
60
    print "    <entry>Method</entry>"
61
    print "    <entry>Description</entry>"
62
    print "  </row>"
63
    print "</thead>"
64
    print '<tbody valign="top">'
78
      print "::\n"
79
      print indent(description.strip())
80
    print
81
    supported = [cmd for cmd in CHECKED_COMMANDS if hasattr(cls, cmd)]
82
    print "It supports the following commands: %s." % (", ".join(supported))
83
    print
65 84

  
66 85
    for cmd in CHECKED_COMMANDS:
67 86
      if not hasattr(cls, cmd):
68 87
        continue
69 88

  
89
      print "%s\n%s\n" % (cmd, "~" * len(cmd))
90

  
70 91
      # Get docstring
71 92
      text = inspect.getdoc(getattr(cls, cmd))
72
      if not text:
73
        text = ""
74

  
75
      print "<row>"
76
      print "  <entry>%s</entry>" % cgi.escape(cmd)
77
      print ("  <entry><literallayout>%s</literallayout></entry>" %
78
             cgi.escape(text.strip()))
79
      print "</row>"
80

  
81
    print "</tbody>"
82
    print "</tgroup></informaltable>"
83

  
84
    print "</sect2>"
93
      if text:
94
        text = beautify(text)
95
        print "::\n"
96
        print indent(text)
97
        print
85 98

  
86 99

  
87 100
if __name__ == "__main__":
b/doc/rapi.rst
1
Ganeti remote API
2
=================
3

  
4
Documents Ganeti version 2.0
5

  
6
.. contents::
7

  
8
Introduction
9
------------
10

  
11
Ganeti supports a remote API for enable external tools to easily
12
retrieve information about a cluster's state. The remote API daemon,
13
*ganeti-rapi*, is automatically started on the master node. By default
14
it runs on TCP port 5080, but this can be changed either in
15
``.../constants.py`` or via the command line parameter *-p*. SSL mode,
16
which is used by default, can also be disabled by passing command line
17
parameters.
18

  
19
Protocol
20
--------
21

  
22
The protocol used is JSON_ over HTTP designed after the REST_
23
principle.
24

  
25
.. _JSON: http://www.json.org/
26
.. _REST: http://en.wikipedia.org/wiki/Representational_State_Transfer
27

  
28
Usage examples
29
--------------
30

  
31
You can access the API using your favorite programming language as
32
long as it supports network connections.
33

  
34
Shell
35
+++++
36

  
37
Using wget::
38

  
39
  wget -q -O - https://CLUSTERNAME:5080/2/info
40

  
41
or curl::
42

  
43
  curl https://CLUSTERNAME:5080/2/info
44

  
45

  
46
Python
47
++++++
48

  
49
::
50

  
51
  import urllib2
52
  f = urllib2.urlopen('https://CLUSTERNAME:5080/info')
53
  print f.read()
54

  
55

  
56
JavaScript
57
++++++++++
58

  
59
.. warning:: While it's possible to use JavaScript, it poses several potential
60
  problems, including browser blocking request due to
61
  non-standard ports or different domain names. Fetching the data
62
  on the webserver is easier.
63

  
64
::
65

  
66
  var url = 'https://CLUSTERNAME:5080/info';
67
  var info;
68
  var xmlreq = new XMLHttpRequest();
69
  xmlreq.onreadystatechange = function () {
70
    if (xmlreq.readyState != 4) return;
71
    if (xmlreq.status == 200) {
72
      info = eval("(" + xmlreq.responseText + ")");
73
      alert(info);
74
    } else {
75
      alert('Error fetching cluster info');
76
    }
77
    xmlreq = null;
78
  };
79
  xmlreq.open('GET', url, true);
80
  xmlreq.send(null);
81

  
82
Resources
83
---------
84

  
85
.. include:: rapi-resources.gen
/dev/null
1
<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook V4.2//EN" [
2
<!ENTITY JsonLink "http://www.json.org/">
3
<!ENTITY WikipediaRESTLink
4
  "http://en.wikipedia.org/wiki/Representational_State_Transfer">
5
]>
6
<article class="specification">
7
<articleinfo>
8
  <title>Ganeti remote API</title>
9
</articleinfo>
10

  
11
<para>Documents Ganeti version 2.0</para>
12

  
13
<sect1>
14
  <title>Introduction</title>
15

  
16
  <para>Ganeti supports a remote API for enable external tools to
17
  easily retrieve information about a cluster's state. The remote API
18
  daemon, <command>ganeti-rapi</command>, is automatically started on
19
  the master node. By default it runs on TCP port 5080, but this can
20
  be changed either in <filename>&hellip;/constants.py</filename> or
21
  via the command line parameter <option>-p</option>. SSL support can
22
  also be enabled by passing command line parameters.</para>
23

  
24
</sect1>
25

  
26
<sect1>
27
    <title>Protocol</title>
28

  
29
  <para>The protocol used is <ulink url="&JsonLink;">JSON</ulink> over HTTP
30
    designed after the <ulink url="&WikipediaRESTLink;">REST</ulink> principle.
31
  </para>
32
</sect1>
33

  
34
<sect1>
35
  <title>Usage examples</title>
36

  
37
  <para>You can access the API using your favorite programming language as long
38
    as it supports network connections.</para>
39

  
40
  <sect2>
41
      <title>Shell</title>
42
      <screen>wget -q -O - https://<replaceable>CLUSTERNAME</replaceable>:5080/2/info</screen>
43
      <para>or</para>
44
      <screen>curl https://<replaceable>CLUSTERNAME</replaceable>:5080/2/info</screen>
45
  </sect2>
46

  
47
  <sect2>
48
    <title>Python</title>
49
    <screen>import urllib2
50
f = urllib2.urlopen('https://<replaceable>CLUSTERNAME</replaceable>:5080/info')
51
print f.read()</screen>
52
  </sect2>
53

  
54
  <sect2>
55
    <title>JavaScript</title>
56
    <note>
57
      <para>While it's possible to use JavaScript, it poses several potential
58
        problems, including browser blocking request due to non-standard ports
59
        or different domain names. Fetching the data on the webserver is
60
        easier.</para>
61
    </note>
62
    <screen>var url = 'https://<replaceable>CLUSTERNAME</replaceable>:5080/info';
63
var info;
64

  
65
var xmlreq = new XMLHttpRequest();
66
xmlreq.onreadystatechange = function () {
67
  if (xmlreq.readyState != 4) return;
68
  if (xmlreq.status == 200) {
69
    info = eval("(" + xmlreq.responseText + ")");
70
    alert(info);
71
  } else {
72
    alert('Error fetching cluster info');
73
  }
74
  xmlreq = null;
75
};
76
xmlreq.open('GET', url, true);
77
xmlreq.send(null);</screen>
78
  </sect2>
79

  
80
</sect1>
81

  
82
<sect1>
83
  <title>Resources</title>
84
  @INCLUDE_RAPI_RESOURCES@
85
</sect1>
86

  
87
</article>

Also available in: Unified diff