Revision 10b207d4
b/Makefile.am | ||
---|---|---|
94 | 94 |
lib/rapi/__init__.py \ |
95 | 95 |
lib/rapi/RESTHTTPServer.py \ |
96 | 96 |
lib/rapi/httperror.py \ |
97 |
lib/rapi/resources.py |
|
97 |
lib/rapi/baserlib.py \ |
|
98 |
lib/rapi/connector.py \ |
|
99 |
lib/rapi/rlib1.py \ |
|
100 |
lib/rapi/rlib2.py |
|
98 | 101 |
|
99 | 102 |
|
100 | 103 |
docsgml = \ |
... | ... | |
226 | 229 |
|
227 | 230 |
doc/rapi.pdf doc/rapi.html: doc/rapi-resources.sgml |
228 | 231 |
|
229 |
doc/rapi-resources.sgml: $(BUILD_RAPI_RESOURCE_DOC) lib/rapi/resources.py
|
|
232 |
doc/rapi-resources.sgml: $(BUILD_RAPI_RESOURCE_DOC) lib/rapi/connector.py
|
|
230 | 233 |
PYTHONPATH=.:$(top_builddir) $(BUILD_RAPI_RESOURCE_DOC) > $@ || rm -f $@ |
231 | 234 |
|
232 | 235 |
man/%.7: man/%.in man/footer.sgml $(DOCBOOK_WRAPPER) |
b/doc/build-rapi-resources-doc | ||
---|---|---|
26 | 26 |
import cgi |
27 | 27 |
import inspect |
28 | 28 |
|
29 |
from ganeti.rapi import resources |
|
29 |
from ganeti.rapi import rlib1 |
|
30 |
from ganeti.rapi import rlib2 |
|
31 |
from ganeti.rapi import connector |
|
30 | 32 |
|
31 | 33 |
|
32 | 34 |
CHECKED_COMMANDS = ["GET", "POST", "PUT", "DELETE"] |
... | ... | |
34 | 36 |
|
35 | 37 |
def main(): |
36 | 38 |
# Get list of all resources |
37 |
all = list(resources._CONNECTOR.itervalues())
|
|
39 |
all = list(connector.CONNECTOR.itervalues())
|
|
38 | 40 |
|
39 |
# Sort resources by URI
|
|
41 |
# Sort rlib1 by URI
|
|
40 | 42 |
all.sort(cmp=lambda a, b: cmp(a.DOC_URI, b.DOC_URI)) |
41 | 43 |
|
42 | 44 |
print "<!-- Automatically generated, do not edit -->" |
b/lib/rapi/RESTHTTPServer.py | ||
---|---|---|
20 | 20 |
|
21 | 21 |
""" |
22 | 22 |
|
23 |
import socket |
|
24 | 23 |
import BaseHTTPServer |
25 | 24 |
import OpenSSL |
25 |
import re |
|
26 |
import socket |
|
26 | 27 |
import time |
27 | 28 |
|
28 | 29 |
from ganeti import constants |
... | ... | |
30 | 31 |
from ganeti import logger |
31 | 32 |
from ganeti import rpc |
32 | 33 |
from ganeti import serializer |
33 |
from ganeti.rapi import resources |
|
34 |
|
|
35 |
from ganeti.rapi import connector |
|
34 | 36 |
from ganeti.rapi import httperror |
35 | 37 |
|
36 | 38 |
|
... | ... | |
158 | 160 |
self.connection = self.request |
159 | 161 |
self.rfile = socket._fileobject(self.request, "rb", self.rbufsize) |
160 | 162 |
self.wfile = socket._fileobject(self.request, "wb", self.wbufsize) |
161 |
self._resmap = resources.Mapper()
|
|
163 |
self._resmap = connector.Mapper()
|
|
162 | 164 |
|
163 | 165 |
def handle_one_request(self): |
164 | 166 |
"""Handle a single REST request. |
b/lib/rapi/connector.py | ||
---|---|---|
1 |
# |
|
2 |
# |
|
3 |
|
|
4 |
# Copyright (C) 2006, 2007, 2008 Google Inc. |
|
5 |
# |
|
6 |
# This program is free software; you can redistribute it and/or modify |
|
7 |
# it under the terms of the GNU General Public License as published by |
|
8 |
# the Free Software Foundation; either version 2 of the License, or |
|
9 |
# (at your option) any later version. |
|
10 |
# |
|
11 |
# This program is distributed in the hope that it will be useful, but |
|
12 |
# WITHOUT ANY WARRANTY; without even the implied warranty of |
|
13 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
14 |
# General Public License for more details. |
|
15 |
# |
|
16 |
# You should have received a copy of the GNU General Public License |
|
17 |
# along with this program; if not, write to the Free Software |
|
18 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
|
19 |
# 02110-1301, USA. |
|
20 |
|
|
21 |
"""Remote API connection map. |
|
22 |
|
|
23 |
""" |
|
24 |
|
|
25 |
import cgi |
|
26 |
import re |
|
27 |
|
|
28 |
from ganeti import constants |
|
29 |
|
|
30 |
from ganeti.rapi import baserlib |
|
31 |
from ganeti.rapi import httperror |
|
32 |
from ganeti.rapi import rlib1 |
|
33 |
from ganeti.rapi import rlib2 |
|
34 |
|
|
35 |
# the connection map created at the end of this file |
|
36 |
CONNECTOR = {} |
|
37 |
|
|
38 |
|
|
39 |
class Mapper: |
|
40 |
"""Map resource to method. |
|
41 |
|
|
42 |
""" |
|
43 |
def __init__(self, connector=CONNECTOR): |
|
44 |
"""Resource mapper constructor. |
|
45 |
|
|
46 |
Args: |
|
47 |
con: a dictionary, mapping method name with URL path regexp |
|
48 |
|
|
49 |
""" |
|
50 |
self._connector = connector |
|
51 |
|
|
52 |
def getController(self, uri): |
|
53 |
"""Find method for a given URI. |
|
54 |
|
|
55 |
Args: |
|
56 |
uri: string with URI |
|
57 |
|
|
58 |
Returns: |
|
59 |
None if no method is found or a tuple containing the following fields: |
|
60 |
methd: name of method mapped to URI |
|
61 |
items: a list of variable intems in the path |
|
62 |
args: a dictionary with additional parameters from URL |
|
63 |
|
|
64 |
""" |
|
65 |
if '?' in uri: |
|
66 |
(path, query) = uri.split('?', 1) |
|
67 |
args = cgi.parse_qs(query) |
|
68 |
else: |
|
69 |
path = uri |
|
70 |
query = None |
|
71 |
args = {} |
|
72 |
|
|
73 |
result = None |
|
74 |
|
|
75 |
for key, handler in self._connector.iteritems(): |
|
76 |
# Regex objects |
|
77 |
if hasattr(key, "match"): |
|
78 |
m = key.match(path) |
|
79 |
if m: |
|
80 |
result = (handler, list(m.groups()), args) |
|
81 |
break |
|
82 |
|
|
83 |
# String objects |
|
84 |
elif key == path: |
|
85 |
result = (handler, [], args) |
|
86 |
break |
|
87 |
|
|
88 |
if result is not None: |
|
89 |
return result |
|
90 |
else: |
|
91 |
raise httperror.HTTPNotFound() |
|
92 |
|
|
93 |
|
|
94 |
class R_root(baserlib.R_Generic): |
|
95 |
"""/ resource. |
|
96 |
|
|
97 |
""" |
|
98 |
DOC_URI = "/" |
|
99 |
|
|
100 |
def GET(self): |
|
101 |
"""Show the list of mapped resources. |
|
102 |
|
|
103 |
Returns: |
|
104 |
A dictionary with 'name' and 'uri' keys for each of them. |
|
105 |
|
|
106 |
""" |
|
107 |
root_pattern = re.compile('^R_([a-zA-Z0-9]+)$') |
|
108 |
|
|
109 |
rootlist = [] |
|
110 |
for handler in CONNECTOR.values(): |
|
111 |
m = root_pattern.match(handler.__name__) |
|
112 |
if m: |
|
113 |
name = m.group(1) |
|
114 |
if name != 'root': |
|
115 |
rootlist.append(name) |
|
116 |
|
|
117 |
return baserlib.BuildUriList(rootlist, "/%s") |
|
118 |
|
|
119 |
|
|
120 |
CONNECTOR.update({ |
|
121 |
"/": R_root, |
|
122 |
|
|
123 |
"/version": rlib1.R_version, |
|
124 |
|
|
125 |
"/tags": rlib1.R_tags, |
|
126 |
"/info": rlib1.R_info, |
|
127 |
|
|
128 |
"/nodes": rlib1.R_nodes, |
|
129 |
re.compile(r'^/nodes/([\w\._-]+)$'): rlib1.R_nodes_name, |
|
130 |
re.compile(r'^/nodes/([\w\._-]+)/tags$'): rlib1.R_nodes_name_tags, |
|
131 |
|
|
132 |
"/instances": rlib1.R_instances, |
|
133 |
re.compile(r'^/instances/([\w\._-]+)$'): rlib1.R_instances_name, |
|
134 |
re.compile(r'^/instances/([\w\._-]+)/tags$'): rlib1.R_instances_name_tags, |
|
135 |
|
|
136 |
"/os": rlib1.R_os, |
|
137 |
|
|
138 |
"/2/jobs": rlib2.R_2_jobs, |
|
139 |
"/2/nodes": rlib2.R_2_nodes, |
|
140 |
re.compile(r'/2/jobs/(%s)$' % constants.JOB_ID_TEMPLATE): rlib2.R_2_jobs_id, |
|
141 |
}) |
/dev/null | ||
---|---|---|
1 |
# |
|
2 |
# |
|
3 |
|
|
4 |
# Copyright (C) 2006, 2007, 2008 Google Inc. |
|
5 |
# |
|
6 |
# This program is free software; you can redistribute it and/or modify |
|
7 |
# it under the terms of the GNU General Public License as published by |
|
8 |
# the Free Software Foundation; either version 2 of the License, or |
|
9 |
# (at your option) any later version. |
|
10 |
# |
|
11 |
# This program is distributed in the hope that it will be useful, but |
|
12 |
# WITHOUT ANY WARRANTY; without even the implied warranty of |
|
13 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
14 |
# General Public License for more details. |
|
15 |
# |
|
16 |
# You should have received a copy of the GNU General Public License |
|
17 |
# along with this program; if not, write to the Free Software |
|
18 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
|
19 |
# 02110-1301, USA. |
|
20 |
|
|
21 |
|
|
22 |
"""Remote API resources. |
|
23 |
|
|
24 |
""" |
|
25 |
|
|
26 |
import cgi |
|
27 |
import re |
|
28 |
|
|
29 |
import ganeti.opcodes |
|
30 |
import ganeti.errors |
|
31 |
import ganeti.cli |
|
32 |
|
|
33 |
from ganeti import constants |
|
34 |
from ganeti import luxi |
|
35 |
from ganeti import utils |
|
36 |
from ganeti.rapi import httperror |
|
37 |
|
|
38 |
|
|
39 |
# Initialized at the end of this file. |
|
40 |
_CONNECTOR = {} |
|
41 |
|
|
42 |
|
|
43 |
def BuildUriList(ids, uri_format, uri_fields=("name", "uri")): |
|
44 |
"""Builds a URI list as used by index resources. |
|
45 |
|
|
46 |
Args: |
|
47 |
- ids: List of ids as strings |
|
48 |
- uri_format: Format to be applied for URI |
|
49 |
- uri_fields: Optional parameter for field ids |
|
50 |
|
|
51 |
""" |
|
52 |
(field_id, field_uri) = uri_fields |
|
53 |
|
|
54 |
def _MapId(m_id): |
|
55 |
return { field_id: m_id, field_uri: uri_format % m_id, } |
|
56 |
|
|
57 |
# Make sure the result is sorted, makes it nicer to look at and simplifies |
|
58 |
# unittests. |
|
59 |
ids.sort() |
|
60 |
|
|
61 |
return map(_MapId, ids) |
|
62 |
|
|
63 |
|
|
64 |
def ExtractField(sequence, index): |
|
65 |
"""Creates a list containing one column out of a list of lists. |
|
66 |
|
|
67 |
Args: |
|
68 |
- sequence: Sequence of lists |
|
69 |
- index: Index of field |
|
70 |
|
|
71 |
""" |
|
72 |
return map(lambda item: item[index], sequence) |
|
73 |
|
|
74 |
|
|
75 |
def MapFields(names, data): |
|
76 |
"""Maps two lists into one dictionary. |
|
77 |
|
|
78 |
Args: |
|
79 |
- names: Field names (list of strings) |
|
80 |
- data: Field data (list) |
|
81 |
|
|
82 |
Example: |
|
83 |
>>> MapFields(["a", "b"], ["foo", 123]) |
|
84 |
{'a': 'foo', 'b': 123} |
|
85 |
|
|
86 |
""" |
|
87 |
if len(names) != len(data): |
|
88 |
raise AttributeError("Names and data must have the same length") |
|
89 |
return dict([(names[i], data[i]) for i in range(len(names))]) |
|
90 |
|
|
91 |
|
|
92 |
def _Tags_GET(kind, name=None): |
|
93 |
"""Helper function to retrieve tags. |
|
94 |
|
|
95 |
""" |
|
96 |
if name is None: |
|
97 |
# Do not cause "missing parameter" error, which happens if a parameter |
|
98 |
# is None. |
|
99 |
name = "" |
|
100 |
op = ganeti.opcodes.OpGetTags(kind=kind, name=name) |
|
101 |
tags = ganeti.cli.SubmitOpCode(op) |
|
102 |
return list(tags) |
|
103 |
|
|
104 |
|
|
105 |
class Mapper: |
|
106 |
"""Map resource to method. |
|
107 |
|
|
108 |
""" |
|
109 |
def __init__(self, connector=_CONNECTOR): |
|
110 |
"""Resource mapper constructor. |
|
111 |
|
|
112 |
Args: |
|
113 |
con: a dictionary, mapping method name with URL path regexp |
|
114 |
|
|
115 |
""" |
|
116 |
self._connector = connector |
|
117 |
|
|
118 |
def getController(self, uri): |
|
119 |
"""Find method for a given URI. |
|
120 |
|
|
121 |
Args: |
|
122 |
uri: string with URI |
|
123 |
|
|
124 |
Returns: |
|
125 |
None if no method is found or a tuple containing the following fields: |
|
126 |
methd: name of method mapped to URI |
|
127 |
items: a list of variable intems in the path |
|
128 |
args: a dictionary with additional parameters from URL |
|
129 |
|
|
130 |
""" |
|
131 |
if '?' in uri: |
|
132 |
(path, query) = uri.split('?', 1) |
|
133 |
args = cgi.parse_qs(query) |
|
134 |
else: |
|
135 |
path = uri |
|
136 |
query = None |
|
137 |
args = {} |
|
138 |
|
|
139 |
result = None |
|
140 |
|
|
141 |
for key, handler in self._connector.iteritems(): |
|
142 |
# Regex objects |
|
143 |
if hasattr(key, "match"): |
|
144 |
m = key.match(path) |
|
145 |
if m: |
|
146 |
result = (handler, list(m.groups()), args) |
|
147 |
break |
|
148 |
|
|
149 |
# String objects |
|
150 |
elif key == path: |
|
151 |
result = (handler, [], args) |
|
152 |
break |
|
153 |
|
|
154 |
if result is not None: |
|
155 |
return result |
|
156 |
else: |
|
157 |
raise httperror.HTTPNotFound() |
|
158 |
|
|
159 |
|
|
160 |
class R_Generic(object): |
|
161 |
"""Generic class for resources. |
|
162 |
|
|
163 |
""" |
|
164 |
def __init__(self, request, items, queryargs): |
|
165 |
"""Generic resource constructor. |
|
166 |
|
|
167 |
Args: |
|
168 |
request: HTTPRequestHandler object |
|
169 |
items: a list with variables encoded in the URL |
|
170 |
queryargs: a dictionary with additional options from URL |
|
171 |
|
|
172 |
""" |
|
173 |
self.request = request |
|
174 |
self.items = items |
|
175 |
self.queryargs = queryargs |
|
176 |
|
|
177 |
|
|
178 |
class R_root(R_Generic): |
|
179 |
"""/ resource. |
|
180 |
|
|
181 |
""" |
|
182 |
DOC_URI = "/" |
|
183 |
|
|
184 |
def GET(self): |
|
185 |
"""Show the list of mapped resources. |
|
186 |
|
|
187 |
Returns: |
|
188 |
A dictionary with 'name' and 'uri' keys for each of them. |
|
189 |
|
|
190 |
""" |
|
191 |
root_pattern = re.compile('^R_([a-zA-Z0-9]+)$') |
|
192 |
|
|
193 |
rootlist = [] |
|
194 |
for handler in _CONNECTOR.values(): |
|
195 |
m = root_pattern.match(handler.__name__) |
|
196 |
if m: |
|
197 |
name = m.group(1) |
|
198 |
if name != 'root': |
|
199 |
rootlist.append(name) |
|
200 |
|
|
201 |
return BuildUriList(rootlist, "/%s") |
|
202 |
|
|
203 |
|
|
204 |
class R_version(R_Generic): |
|
205 |
"""/version resource. |
|
206 |
|
|
207 |
This resource should be used to determine the remote API version and to adapt |
|
208 |
clients accordingly. |
|
209 |
|
|
210 |
""" |
|
211 |
DOC_URI = "/version" |
|
212 |
|
|
213 |
def GET(self): |
|
214 |
"""Returns the remote API version. |
|
215 |
|
|
216 |
""" |
|
217 |
return constants.RAPI_VERSION |
|
218 |
|
|
219 |
|
|
220 |
class R_tags(R_Generic): |
|
221 |
"""/tags resource. |
|
222 |
|
|
223 |
Manages cluster tags. |
|
224 |
|
|
225 |
""" |
|
226 |
DOC_URI = "/tags" |
|
227 |
|
|
228 |
def GET(self): |
|
229 |
"""Returns a list of all cluster tags. |
|
230 |
|
|
231 |
Example: ["tag1", "tag2", "tag3"] |
|
232 |
|
|
233 |
""" |
|
234 |
return _Tags_GET(constants.TAG_CLUSTER) |
|
235 |
|
|
236 |
|
|
237 |
class R_info(R_Generic): |
|
238 |
"""Cluster info. |
|
239 |
|
|
240 |
""" |
|
241 |
DOC_URI = "/info" |
|
242 |
|
|
243 |
def GET(self): |
|
244 |
"""Returns cluster information. |
|
245 |
|
|
246 |
Example: { |
|
247 |
"config_version": 3, |
|
248 |
"name": "cluster1.example.com", |
|
249 |
"software_version": "1.2.4", |
|
250 |
"os_api_version": 5, |
|
251 |
"export_version": 0, |
|
252 |
"master": "node1.example.com", |
|
253 |
"architecture": [ |
|
254 |
"64bit", |
|
255 |
"x86_64" |
|
256 |
], |
|
257 |
"hypervisor_type": "xen-3.0", |
|
258 |
"protocol_version": 12 |
|
259 |
} |
|
260 |
|
|
261 |
""" |
|
262 |
op = ganeti.opcodes.OpQueryClusterInfo() |
|
263 |
return ganeti.cli.SubmitOpCode(op) |
|
264 |
|
|
265 |
|
|
266 |
class R_nodes(R_Generic): |
|
267 |
"""/nodes resource. |
|
268 |
|
|
269 |
""" |
|
270 |
DOC_URI = "/nodes" |
|
271 |
|
|
272 |
def _GetDetails(self, nodeslist): |
|
273 |
"""Returns detailed instance data for bulk output. |
|
274 |
|
|
275 |
Args: |
|
276 |
instance: A list of nodes names. |
|
277 |
|
|
278 |
Returns: |
|
279 |
A list of nodes properties |
|
280 |
|
|
281 |
""" |
|
282 |
fields = ["name","dtotal", "dfree", |
|
283 |
"mtotal", "mnode", "mfree", |
|
284 |
"pinst_cnt", "sinst_cnt", "tags"] |
|
285 |
|
|
286 |
op = ganeti.opcodes.OpQueryNodes(output_fields=fields, |
|
287 |
names=nodeslist) |
|
288 |
result = ganeti.cli.SubmitOpCode(op) |
|
289 |
|
|
290 |
nodes_details = [] |
|
291 |
for node in result: |
|
292 |
mapped = MapFields(fields, node) |
|
293 |
nodes_details.append(mapped) |
|
294 |
return nodes_details |
|
295 |
|
|
296 |
def GET(self): |
|
297 |
"""Returns a list of all nodes. |
|
298 |
|
|
299 |
Returns: |
|
300 |
A dictionary with 'name' and 'uri' keys for each of them. |
|
301 |
|
|
302 |
Example: [ |
|
303 |
{ |
|
304 |
"name": "node1.example.com", |
|
305 |
"uri": "\/instances\/node1.example.com" |
|
306 |
}, |
|
307 |
{ |
|
308 |
"name": "node2.example.com", |
|
309 |
"uri": "\/instances\/node2.example.com" |
|
310 |
}] |
|
311 |
|
|
312 |
If the optional 'bulk' argument is provided and set to 'true' |
|
313 |
value (i.e '?bulk=1'), the output contains detailed |
|
314 |
information about nodes as a list. |
|
315 |
|
|
316 |
Example: [ |
|
317 |
{ |
|
318 |
"pinst_cnt": 1, |
|
319 |
"mfree": 31280, |
|
320 |
"mtotal": 32763, |
|
321 |
"name": "www.example.com", |
|
322 |
"tags": [], |
|
323 |
"mnode": 512, |
|
324 |
"dtotal": 5246208, |
|
325 |
"sinst_cnt": 2, |
|
326 |
"dfree": 5171712 |
|
327 |
}, |
|
328 |
... |
|
329 |
] |
|
330 |
|
|
331 |
""" |
|
332 |
op = ganeti.opcodes.OpQueryNodes(output_fields=["name"], names=[]) |
|
333 |
nodeslist = ExtractField(ganeti.cli.SubmitOpCode(op), 0) |
|
334 |
|
|
335 |
if 'bulk' in self.queryargs: |
|
336 |
return self._GetDetails(nodeslist) |
|
337 |
|
|
338 |
return BuildUriList(nodeslist, "/nodes/%s") |
|
339 |
|
|
340 |
|
|
341 |
class R_nodes_name(R_Generic): |
|
342 |
"""/nodes/[node_name] resources. |
|
343 |
|
|
344 |
""" |
|
345 |
DOC_URI = "/nodes/[node_name]" |
|
346 |
|
|
347 |
def GET(self): |
|
348 |
"""Send information about a node. |
|
349 |
|
|
350 |
""" |
|
351 |
node_name = self.items[0] |
|
352 |
fields = ["name","dtotal", "dfree", |
|
353 |
"mtotal", "mnode", "mfree", |
|
354 |
"pinst_cnt", "sinst_cnt", "tags"] |
|
355 |
|
|
356 |
op = ganeti.opcodes.OpQueryNodes(output_fields=fields, |
|
357 |
names=[node_name]) |
|
358 |
result = ganeti.cli.SubmitOpCode(op) |
|
359 |
|
|
360 |
return MapFields(fields, result[0]) |
|
361 |
|
|
362 |
|
|
363 |
class R_nodes_name_tags(R_Generic): |
|
364 |
"""/nodes/[node_name]/tags resource. |
|
365 |
|
|
366 |
Manages per-node tags. |
|
367 |
|
|
368 |
""" |
|
369 |
DOC_URI = "/nodes/[node_name]/tags" |
|
370 |
|
|
371 |
def GET(self): |
|
372 |
"""Returns a list of node tags. |
|
373 |
|
|
374 |
Example: ["tag1", "tag2", "tag3"] |
|
375 |
|
|
376 |
""" |
|
377 |
return _Tags_GET(constants.TAG_NODE, name=self.items[0]) |
|
378 |
|
|
379 |
|
|
380 |
class R_instances(R_Generic): |
|
381 |
"""/instances resource. |
|
382 |
|
|
383 |
""" |
|
384 |
DOC_URI = "/instances" |
|
385 |
|
|
386 |
def _GetDetails(self, instanceslist): |
|
387 |
"""Returns detailed instance data for bulk output. |
|
388 |
|
|
389 |
Args: |
|
390 |
instance: A list of instances names. |
|
391 |
|
|
392 |
Returns: |
|
393 |
A list with instances properties. |
|
394 |
|
|
395 |
""" |
|
396 |
fields = ["name", "os", "pnode", "snodes", |
|
397 |
"admin_state", "admin_ram", |
|
398 |
"disk_template", "ip", "mac", "bridge", |
|
399 |
"sda_size", "sdb_size", "vcpus", |
|
400 |
"oper_state", "status", "tags"] |
|
401 |
|
|
402 |
op = ganeti.opcodes.OpQueryInstances(output_fields=fields, |
|
403 |
names=instanceslist) |
|
404 |
result = ganeti.cli.SubmitOpCode(op) |
|
405 |
|
|
406 |
instances_details = [] |
|
407 |
for instance in result: |
|
408 |
mapped = MapFields(fields, instance) |
|
409 |
instances_details.append(mapped) |
|
410 |
return instances_details |
|
411 |
|
|
412 |
def GET(self): |
|
413 |
"""Returns a list of all available instances. |
|
414 |
|
|
415 |
Returns: |
|
416 |
A dictionary with 'name' and 'uri' keys for each of them. |
|
417 |
|
|
418 |
Example: [ |
|
419 |
{ |
|
420 |
"name": "web.example.com", |
|
421 |
"uri": "\/instances\/web.example.com" |
|
422 |
}, |
|
423 |
{ |
|
424 |
"name": "mail.example.com", |
|
425 |
"uri": "\/instances\/mail.example.com" |
|
426 |
}] |
|
427 |
|
|
428 |
If the optional 'bulk' argument is provided and set to 'true' |
|
429 |
value (i.e '?bulk=1'), the output contains detailed |
|
430 |
information about instances as a list. |
|
431 |
|
|
432 |
Example: [ |
|
433 |
{ |
|
434 |
"status": "running", |
|
435 |
"bridge": "xen-br0", |
|
436 |
"name": "web.example.com", |
|
437 |
"tags": ["tag1", "tag2"], |
|
438 |
"admin_ram": 512, |
|
439 |
"sda_size": 20480, |
|
440 |
"pnode": "node1.example.com", |
|
441 |
"mac": "01:23:45:67:89:01", |
|
442 |
"sdb_size": 4096, |
|
443 |
"snodes": ["node2.example.com"], |
|
444 |
"disk_template": "drbd", |
|
445 |
"ip": null, |
|
446 |
"admin_state": true, |
|
447 |
"os": "debian-etch", |
|
448 |
"vcpus": 2, |
|
449 |
"oper_state": true |
|
450 |
}, |
|
451 |
... |
|
452 |
] |
|
453 |
|
|
454 |
""" |
|
455 |
op = ganeti.opcodes.OpQueryInstances(output_fields=["name"], names=[]) |
|
456 |
instanceslist = ExtractField(ganeti.cli.SubmitOpCode(op), 0) |
|
457 |
|
|
458 |
if 'bulk' in self.queryargs: |
|
459 |
return self._GetDetails(instanceslist) |
|
460 |
|
|
461 |
else: |
|
462 |
return BuildUriList(instanceslist, "/instances/%s") |
|
463 |
|
|
464 |
|
|
465 |
class R_instances_name(R_Generic): |
|
466 |
"""/instances/[instance_name] resources. |
|
467 |
|
|
468 |
""" |
|
469 |
DOC_URI = "/instances/[instance_name]" |
|
470 |
|
|
471 |
def GET(self): |
|
472 |
"""Send information about an instance. |
|
473 |
|
|
474 |
""" |
|
475 |
instance_name = self.items[0] |
|
476 |
fields = ["name", "os", "pnode", "snodes", |
|
477 |
"admin_state", "admin_ram", |
|
478 |
"disk_template", "ip", "mac", "bridge", |
|
479 |
"sda_size", "sdb_size", "vcpus", |
|
480 |
"oper_state", "status", "tags"] |
|
481 |
|
|
482 |
op = ganeti.opcodes.OpQueryInstances(output_fields=fields, |
|
483 |
names=[instance_name]) |
|
484 |
result = ganeti.cli.SubmitOpCode(op) |
|
485 |
|
|
486 |
return MapFields(fields, result[0]) |
|
487 |
|
|
488 |
|
|
489 |
class R_instances_name_tags(R_Generic): |
|
490 |
"""/instances/[instance_name]/tags resource. |
|
491 |
|
|
492 |
Manages per-instance tags. |
|
493 |
|
|
494 |
""" |
|
495 |
DOC_URI = "/instances/[instance_name]/tags" |
|
496 |
|
|
497 |
def GET(self): |
|
498 |
"""Returns a list of instance tags. |
|
499 |
|
|
500 |
Example: ["tag1", "tag2", "tag3"] |
|
501 |
|
|
502 |
""" |
|
503 |
return _Tags_GET(constants.TAG_INSTANCE, name=self.items[0]) |
|
504 |
|
|
505 |
|
|
506 |
class R_os(R_Generic): |
|
507 |
"""/os resource. |
|
508 |
|
|
509 |
""" |
|
510 |
DOC_URI = "/os" |
|
511 |
|
|
512 |
def GET(self): |
|
513 |
"""Return a list of all OSes. |
|
514 |
|
|
515 |
Can return error 500 in case of a problem. |
|
516 |
|
|
517 |
Example: ["debian-etch"] |
|
518 |
|
|
519 |
""" |
|
520 |
op = ganeti.opcodes.OpDiagnoseOS(output_fields=["name", "valid"], |
|
521 |
names=[]) |
|
522 |
diagnose_data = ganeti.cli.SubmitOpCode(op) |
|
523 |
|
|
524 |
if not isinstance(diagnose_data, list): |
|
525 |
raise httperror.HTTPInternalError(message="Can't get OS list") |
|
526 |
|
|
527 |
return [row[0] for row in diagnose_data if row[1]] |
|
528 |
|
|
529 |
|
|
530 |
class R_2_jobs(R_Generic): |
|
531 |
"""/2/jobs resource. |
|
532 |
|
|
533 |
""" |
|
534 |
DOC_URI = "/2/jobs" |
|
535 |
|
|
536 |
def GET(self): |
|
537 |
"""Returns a dictionary of jobs. |
|
538 |
|
|
539 |
Returns: |
|
540 |
A dictionary with jobs id and uri. |
|
541 |
|
|
542 |
""" |
|
543 |
fields = ["id"] |
|
544 |
# Convert the list of lists to the list of ids |
|
545 |
result = [job_id for [job_id] in luxi.Client().QueryJobs(None, fields)] |
|
546 |
return BuildUriList(result, "/2/jobs/%s", uri_fields=("id", "uri")) |
|
547 |
|
|
548 |
|
|
549 |
class R_2_jobs_id(R_Generic): |
|
550 |
"""/2/jobs/[job_id] resource. |
|
551 |
|
|
552 |
""" |
|
553 |
DOC_URI = "/2/jobs/[job_id]" |
|
554 |
|
|
555 |
def GET(self): |
|
556 |
"""Returns a job status. |
|
557 |
|
|
558 |
Returns: |
|
559 |
A dictionary with job parameters. |
|
560 |
|
|
561 |
The result includes: |
|
562 |
id - job ID as a number |
|
563 |
status - current job status as a string |
|
564 |
ops - involved OpCodes as a list of dictionaries for each opcodes in |
|
565 |
the job |
|
566 |
opstatus - OpCodes status as a list |
|
567 |
opresult - OpCodes results as a list of lists |
|
568 |
|
|
569 |
""" |
|
570 |
fields = ["id", "ops", "status", "opstatus", "opresult"] |
|
571 |
job_id = self.items[0] |
|
572 |
result = luxi.Client().QueryJobs([job_id,], fields)[0] |
|
573 |
return MapFields(fields, result) |
|
574 |
|
|
575 |
|
|
576 |
_CONNECTOR.update({ |
|
577 |
"/": R_root, |
|
578 |
|
|
579 |
"/version": R_version, |
|
580 |
|
|
581 |
"/tags": R_tags, |
|
582 |
"/info": R_info, |
|
583 |
|
|
584 |
"/nodes": R_nodes, |
|
585 |
re.compile(r'^/nodes/([\w\._-]+)$'): R_nodes_name, |
|
586 |
re.compile(r'^/nodes/([\w\._-]+)/tags$'): R_nodes_name_tags, |
|
587 |
|
|
588 |
"/instances": R_instances, |
|
589 |
re.compile(r'^/instances/([\w\._-]+)$'): R_instances_name, |
|
590 |
re.compile(r'^/instances/([\w\._-]+)/tags$'): R_instances_name_tags, |
|
591 |
|
|
592 |
"/os": R_os, |
|
593 |
|
|
594 |
"/2/jobs": R_2_jobs, |
|
595 |
re.compile(r'/2/jobs/(%s)$' % constants.JOB_ID_TEMPLATE): R_2_jobs_id, |
|
596 |
}) |
b/lib/rapi/rlib1.py | ||
---|---|---|
1 |
# |
|
2 |
# |
|
3 |
|
|
4 |
# Copyright (C) 2006, 2007, 2008 Google Inc. |
|
5 |
# |
|
6 |
# This program is free software; you can redistribute it and/or modify |
|
7 |
# it under the terms of the GNU General Public License as published by |
|
8 |
# the Free Software Foundation; either version 2 of the License, or |
|
9 |
# (at your option) any later version. |
|
10 |
# |
|
11 |
# This program is distributed in the hope that it will be useful, but |
|
12 |
# WITHOUT ANY WARRANTY; without even the implied warranty of |
|
13 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
14 |
# General Public License for more details. |
|
15 |
# |
|
16 |
# You should have received a copy of the GNU General Public License |
|
17 |
# along with this program; if not, write to the Free Software |
|
18 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
|
19 |
# 02110-1301, USA. |
|
20 |
|
|
21 |
|
|
22 |
"""Remote API version 1 resources library. |
|
23 |
|
|
24 |
""" |
|
25 |
|
|
26 |
import re |
|
27 |
|
|
28 |
import ganeti.cli |
|
29 |
import ganeti.errors |
|
30 |
import ganeti.opcodes |
|
31 |
|
|
32 |
from ganeti import constants |
|
33 |
from ganeti import utils |
|
34 |
|
|
35 |
from ganeti.rapi import baserlib |
|
36 |
from ganeti.rapi import httperror |
|
37 |
|
|
38 |
|
|
39 |
class R_version(baserlib.R_Generic): |
|
40 |
"""/version resource. |
|
41 |
|
|
42 |
This resource should be used to determine the remote API version and to adapt |
|
43 |
clients accordingly. |
|
44 |
|
|
45 |
""" |
|
46 |
DOC_URI = "/version" |
|
47 |
|
|
48 |
def GET(self): |
|
49 |
"""Returns the remote API version. |
|
50 |
|
|
51 |
""" |
|
52 |
return constants.RAPI_VERSION |
|
53 |
|
|
54 |
|
|
55 |
class R_tags(baserlib.R_Generic): |
|
56 |
"""/tags resource. |
|
57 |
|
|
58 |
Manages cluster tags. |
|
59 |
|
|
60 |
""" |
|
61 |
DOC_URI = "/tags" |
|
62 |
|
|
63 |
def GET(self): |
|
64 |
"""Returns a list of all cluster tags. |
|
65 |
|
|
66 |
Example: ["tag1", "tag2", "tag3"] |
|
67 |
|
|
68 |
""" |
|
69 |
return baserlib._Tags_GET(constants.TAG_CLUSTER) |
|
70 |
|
|
71 |
|
|
72 |
class R_info(baserlib.R_Generic): |
|
73 |
"""Cluster info. |
|
74 |
|
|
75 |
""" |
|
76 |
DOC_URI = "/info" |
|
77 |
|
|
78 |
def GET(self): |
|
79 |
"""Returns cluster information. |
|
80 |
|
|
81 |
Example: { |
|
82 |
"config_version": 3, |
|
83 |
"name": "cluster1.example.com", |
|
84 |
"software_version": "1.2.4", |
|
85 |
"os_api_version": 5, |
|
86 |
"export_version": 0, |
|
87 |
"master": "node1.example.com", |
|
88 |
"architecture": [ |
|
89 |
"64bit", |
|
90 |
"x86_64" |
|
91 |
], |
|
92 |
"hypervisor_type": "xen-3.0", |
|
93 |
"protocol_version": 12 |
|
94 |
} |
|
95 |
|
|
96 |
""" |
|
97 |
op = ganeti.opcodes.OpQueryClusterInfo() |
|
98 |
return ganeti.cli.SubmitOpCode(op) |
|
99 |
|
|
100 |
|
|
101 |
class R_nodes(baserlib.R_Generic): |
|
102 |
"""/nodes resource. |
|
103 |
|
|
104 |
""" |
|
105 |
DOC_URI = "/nodes" |
|
106 |
|
|
107 |
def _GetDetails(self, nodeslist): |
|
108 |
"""Returns detailed instance data for bulk output. |
|
109 |
|
|
110 |
Args: |
|
111 |
instance: A list of nodes names. |
|
112 |
|
|
113 |
Returns: |
|
114 |
A list of nodes properties |
|
115 |
|
|
116 |
""" |
|
117 |
fields = ["name","dtotal", "dfree", |
|
118 |
"mtotal", "mnode", "mfree", |
|
119 |
"pinst_cnt", "sinst_cnt", "tags"] |
|
120 |
|
|
121 |
op = ganeti.opcodes.OpQueryNodes(output_fields=fields, |
|
122 |
names=nodeslist) |
|
123 |
result = ganeti.cli.SubmitOpCode(op) |
|
124 |
|
|
125 |
nodes_details = [] |
|
126 |
for node in result: |
|
127 |
mapped = baserlib.MapFields(fields, node) |
|
128 |
nodes_details.append(mapped) |
|
129 |
return nodes_details |
|
130 |
|
|
131 |
def GET(self): |
|
132 |
"""Returns a list of all nodes. |
|
133 |
|
|
134 |
Returns: |
|
135 |
A dictionary with 'name' and 'uri' keys for each of them. |
|
136 |
|
|
137 |
Example: [ |
|
138 |
{ |
|
139 |
"name": "node1.example.com", |
|
140 |
"uri": "\/instances\/node1.example.com" |
|
141 |
}, |
|
142 |
{ |
|
143 |
"name": "node2.example.com", |
|
144 |
"uri": "\/instances\/node2.example.com" |
|
145 |
}] |
|
146 |
|
|
147 |
If the optional 'bulk' argument is provided and set to 'true' |
|
148 |
value (i.e '?bulk=1'), the output contains detailed |
|
149 |
information about nodes as a list. |
|
150 |
|
|
151 |
Example: [ |
|
152 |
{ |
|
153 |
"pinst_cnt": 1, |
|
154 |
"mfree": 31280, |
|
155 |
"mtotal": 32763, |
|
156 |
"name": "www.example.com", |
|
157 |
"tags": [], |
|
158 |
"mnode": 512, |
|
159 |
"dtotal": 5246208, |
|
160 |
"sinst_cnt": 2, |
|
161 |
"dfree": 5171712 |
|
162 |
}, |
|
163 |
... |
|
164 |
] |
|
165 |
|
|
166 |
""" |
|
167 |
op = ganeti.opcodes.OpQueryNodes(output_fields=["name"], names=[]) |
|
168 |
nodeslist = baserlib.ExtractField(ganeti.cli.SubmitOpCode(op), 0) |
|
169 |
|
|
170 |
if 'bulk' in self.queryargs: |
|
171 |
return self._GetDetails(nodeslist) |
|
172 |
|
|
173 |
return baserlib.BuildUriList(nodeslist, "/nodes/%s") |
|
174 |
|
|
175 |
|
|
176 |
class R_nodes_name(baserlib.R_Generic): |
|
177 |
"""/nodes/[node_name] resources. |
|
178 |
|
|
179 |
""" |
|
180 |
DOC_URI = "/nodes/[node_name]" |
|
181 |
|
|
182 |
def GET(self): |
|
183 |
"""Send information about a node. |
|
184 |
|
|
185 |
""" |
|
186 |
node_name = self.items[0] |
|
187 |
fields = ["name","dtotal", "dfree", |
|
188 |
"mtotal", "mnode", "mfree", |
|
189 |
"pinst_cnt", "sinst_cnt", "tags"] |
|
190 |
|
|
191 |
op = ganeti.opcodes.OpQueryNodes(output_fields=fields, |
|
192 |
names=[node_name]) |
|
193 |
result = ganeti.cli.SubmitOpCode(op) |
|
194 |
|
|
195 |
return baserlib.MapFields(fields, result[0]) |
|
196 |
|
|
197 |
|
|
198 |
class R_nodes_name_tags(baserlib.R_Generic): |
|
199 |
"""/nodes/[node_name]/tags resource. |
|
200 |
|
|
201 |
Manages per-node tags. |
|
202 |
|
|
203 |
""" |
|
204 |
DOC_URI = "/nodes/[node_name]/tags" |
|
205 |
|
|
206 |
def GET(self): |
|
207 |
"""Returns a list of node tags. |
|
208 |
|
|
209 |
Example: ["tag1", "tag2", "tag3"] |
|
210 |
|
|
211 |
""" |
|
212 |
return baserlib._Tags_GET(constants.TAG_NODE, name=self.items[0]) |
|
213 |
|
|
214 |
|
|
215 |
class R_instances(baserlib.R_Generic): |
|
216 |
"""/instances resource. |
|
217 |
|
|
218 |
""" |
|
219 |
DOC_URI = "/instances" |
|
220 |
|
|
221 |
def _GetDetails(self, instanceslist): |
|
222 |
"""Returns detailed instance data for bulk output. |
|
223 |
|
|
224 |
Args: |
|
225 |
instance: A list of instances names. |
|
226 |
|
|
227 |
Returns: |
|
228 |
A list with instances properties. |
|
229 |
|
|
230 |
""" |
|
231 |
fields = ["name", "os", "pnode", "snodes", |
|
232 |
"admin_state", "admin_ram", |
|
233 |
"disk_template", "ip", "mac", "bridge", |
|
234 |
"sda_size", "sdb_size", "vcpus", |
|
235 |
"oper_state", "status", "tags"] |
|
236 |
|
|
237 |
op = ganeti.opcodes.OpQueryInstances(output_fields=fields, |
|
238 |
names=instanceslist) |
|
239 |
result = ganeti.cli.SubmitOpCode(op) |
|
240 |
|
|
241 |
instances_details = [] |
|
242 |
for instance in result: |
|
243 |
mapped = baserlib.MapFields(fields, instance) |
|
244 |
instances_details.append(mapped) |
|
245 |
return instances_details |
|
246 |
|
|
247 |
def GET(self): |
|
248 |
"""Returns a list of all available instances. |
|
249 |
|
|
250 |
Returns: |
|
251 |
A dictionary with 'name' and 'uri' keys for each of them. |
|
252 |
|
|
253 |
Example: [ |
|
254 |
{ |
|
255 |
"name": "web.example.com", |
|
256 |
"uri": "\/instances\/web.example.com" |
|
257 |
}, |
|
258 |
{ |
|
259 |
"name": "mail.example.com", |
|
260 |
"uri": "\/instances\/mail.example.com" |
|
261 |
}] |
|
262 |
|
|
263 |
If the optional 'bulk' argument is provided and set to 'true' |
|
264 |
value (i.e '?bulk=1'), the output contains detailed |
|
265 |
information about instances as a list. |
|
266 |
|
|
267 |
Example: [ |
|
268 |
{ |
|
269 |
"status": "running", |
|
270 |
"bridge": "xen-br0", |
|
271 |
"name": "web.example.com", |
|
272 |
"tags": ["tag1", "tag2"], |
|
273 |
"admin_ram": 512, |
|
274 |
"sda_size": 20480, |
|
275 |
"pnode": "node1.example.com", |
|
276 |
"mac": "01:23:45:67:89:01", |
|
277 |
"sdb_size": 4096, |
|
278 |
"snodes": ["node2.example.com"], |
|
279 |
"disk_template": "drbd", |
|
280 |
"ip": null, |
|
281 |
"admin_state": true, |
|
282 |
"os": "debian-etch", |
|
283 |
"vcpus": 2, |
|
284 |
"oper_state": true |
|
285 |
}, |
|
286 |
... |
|
287 |
] |
|
288 |
|
|
289 |
""" |
|
290 |
op = ganeti.opcodes.OpQueryInstances(output_fields=["name"], names=[]) |
|
291 |
instanceslist = baserlib.ExtractField(ganeti.cli.SubmitOpCode(op), 0) |
|
292 |
|
|
293 |
if 'bulk' in self.queryargs: |
|
294 |
return self._GetDetails(instanceslist) |
|
295 |
|
|
296 |
else: |
|
297 |
return baserlib.BuildUriList(instanceslist, "/instances/%s") |
|
298 |
|
|
299 |
|
|
300 |
class R_instances_name(baserlib.R_Generic): |
|
301 |
"""/instances/[instance_name] resources. |
|
302 |
|
|
303 |
""" |
|
304 |
DOC_URI = "/instances/[instance_name]" |
|
305 |
|
|
306 |
def GET(self): |
|
307 |
"""Send information about an instance. |
|
308 |
|
|
309 |
""" |
|
310 |
instance_name = self.items[0] |
|
311 |
fields = ["name", "os", "pnode", "snodes", |
|
312 |
"admin_state", "admin_ram", |
|
313 |
"disk_template", "ip", "mac", "bridge", |
|
314 |
"sda_size", "sdb_size", "vcpus", |
|
315 |
"oper_state", "status", "tags"] |
|
316 |
|
|
317 |
op = ganeti.opcodes.OpQueryInstances(output_fields=fields, |
|
318 |
names=[instance_name]) |
|
319 |
result = ganeti.cli.SubmitOpCode(op) |
|
320 |
|
|
321 |
return baserlib.MapFields(fields, result[0]) |
|
322 |
|
|
323 |
|
|
324 |
class R_instances_name_tags(baserlib.R_Generic): |
|
325 |
"""/instances/[instance_name]/tags resource. |
|
326 |
|
|
327 |
Manages per-instance tags. |
|
328 |
|
|
329 |
""" |
|
330 |
DOC_URI = "/instances/[instance_name]/tags" |
|
331 |
|
|
332 |
def GET(self): |
|
333 |
"""Returns a list of instance tags. |
|
334 |
|
|
335 |
Example: ["tag1", "tag2", "tag3"] |
|
336 |
|
|
337 |
""" |
|
338 |
return baserlib._Tags_GET(constants.TAG_INSTANCE, name=self.items[0]) |
|
339 |
|
|
340 |
|
|
341 |
class R_os(baserlib.R_Generic): |
|
342 |
"""/os resource. |
|
343 |
|
|
344 |
""" |
|
345 |
DOC_URI = "/os" |
|
346 |
|
|
347 |
def GET(self): |
|
348 |
"""Return a list of all OSes. |
|
349 |
|
|
350 |
Can return error 500 in case of a problem. |
|
351 |
|
|
352 |
Example: ["debian-etch"] |
|
353 |
|
|
354 |
""" |
|
355 |
op = ganeti.opcodes.OpDiagnoseOS(output_fields=["name", "valid"], |
|
356 |
names=[]) |
|
357 |
diagnose_data = ganeti.cli.SubmitOpCode(op) |
|
358 |
|
|
359 |
if not isinstance(diagnose_data, list): |
|
360 |
raise httperror.HTTPInternalError(message="Can't get OS list") |
|
361 |
|
|
362 |
return [row[0] for row in diagnose_data if row[1]] |
b/lib/rapi/rlib2.py | ||
---|---|---|
1 |
# |
|
2 |
# |
|
3 |
|
|
4 |
# Copyright (C) 2006, 2007, 2008 Google Inc. |
|
5 |
# |
|
6 |
# This program is free software; you can redistribute it and/or modify |
|
7 |
# it under the terms of the GNU General Public License as published by |
|
8 |
# the Free Software Foundation; either version 2 of the License, or |
|
9 |
# (at your option) any later version. |
|
10 |
# |
|
11 |
# This program is distributed in the hope that it will be useful, but |
|
12 |
# WITHOUT ANY WARRANTY; without even the implied warranty of |
|
13 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
14 |
# General Public License for more details. |
|
15 |
# |
|
16 |
# You should have received a copy of the GNU General Public License |
|
17 |
# along with this program; if not, write to the Free Software |
|
18 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
|
19 |
# 02110-1301, USA. |
|
20 |
|
|
21 |
|
|
22 |
"""Remote API version 2 baserlib.library. |
|
23 |
|
|
24 |
""" |
|
25 |
|
|
26 |
import re |
|
27 |
|
|
28 |
import ganeti.opcodes |
|
29 |
|
|
30 |
from ganeti import constants |
|
31 |
from ganeti import luxi |
|
32 |
|
|
33 |
from ganeti.rapi import baserlib |
|
34 |
|
|
35 |
|
|
36 |
class R_2_jobs(baserlib.R_Generic): |
|
37 |
"""/2/jobs resource. |
|
38 |
|
|
39 |
""" |
|
40 |
DOC_URI = "/2/jobs" |
|
41 |
|
|
42 |
def GET(self): |
|
43 |
"""Returns a dictionary of jobs. |
|
44 |
|
|
45 |
Returns: |
|
46 |
A dictionary with jobs id and uri. |
|
47 |
|
|
48 |
""" |
|
49 |
fields = ["id"] |
|
50 |
# Convert the list of lists to the list of ids |
|
51 |
result = [job_id for [job_id] in luxi.Client().QueryJobs(None, fields)] |
|
52 |
return baserlib.BuildUriList(result, "/2/jobs/%s", uri_fields=("id", "uri")) |
|
53 |
|
|
54 |
|
|
55 |
class R_2_jobs_id(baserlib.R_Generic): |
|
56 |
"""/2/jobs/[job_id] resource. |
|
57 |
|
|
58 |
""" |
|
59 |
DOC_URI = "/2/jobs/[job_id]" |
|
60 |
|
|
61 |
def GET(self): |
|
62 |
"""Returns a job status. |
|
63 |
|
|
64 |
Returns: |
|
65 |
A dictionary with job parameters. |
|
66 |
|
|
67 |
The result includes: |
|
68 |
id - job ID as a number |
|
69 |
status - current job status as a string |
|
70 |
ops - involved OpCodes as a list of dictionaries for each opcodes in |
|
71 |
the job |
|
72 |
opstatus - OpCodes status as a list |
|
73 |
opresult - OpCodes results as a list of lists |
|
74 |
|
|
75 |
""" |
|
76 |
fields = ["id", "ops", "status", "opstatus", "opresult"] |
|
77 |
job_id = self.items[0] |
|
78 |
result = luxi.Client().QueryJobs([job_id,], fields)[0] |
|
79 |
return baserlib.MapFields(fields, result) |
|
80 |
|
|
81 |
|
|
82 |
class R_2_nodes(baserlib.R_Generic): |
|
83 |
"""/2/nodes resource. |
|
84 |
|
|
85 |
""" |
|
86 |
DOC_URI = "/2/nodes" |
|
87 |
|
|
88 |
def _GetDetails(self, nodeslist): |
|
89 |
"""Returns detailed instance data for bulk output. |
|
90 |
|
|
91 |
Args: |
|
92 |
instance: A list of nodes names. |
|
93 |
|
|
94 |
Returns: |
|
95 |
A list of nodes properties |
|
96 |
|
|
97 |
""" |
|
98 |
fields = ["name","dtotal", "dfree", |
|
99 |
"mtotal", "mnode", "mfree", |
|
100 |
"pinst_cnt", "sinst_cnt", "tags"] |
|
101 |
|
|
102 |
op = ganeti.opcodes.OpQueryNodes(output_fields=fields, |
|
103 |
names=nodeslist) |
|
104 |
result = ganeti.cli.SubmitOpCode(op) |
|
105 |
|
|
106 |
nodes_details = [] |
|
107 |
for node in result: |
|
108 |
mapped = baserlib.MapFields(fields, node) |
|
109 |
nodes_details.append(mapped) |
|
110 |
return nodes_details |
|
111 |
|
|
112 |
def GET(self): |
|
113 |
"""Returns a list of all nodes. |
|
114 |
|
|
115 |
Returns: |
|
116 |
A dictionary with 'name' and 'uri' keys for each of them. |
|
117 |
|
|
118 |
Example: [ |
|
119 |
{ |
|
120 |
"id": "node1.example.com", |
|
121 |
"uri": "\/instances\/node1.example.com" |
|
122 |
}, |
|
123 |
{ |
|
124 |
"id": "node2.example.com", |
|
125 |
"uri": "\/instances\/node2.example.com" |
|
126 |
}] |
|
127 |
|
|
128 |
If the optional 'bulk' argument is provided and set to 'true' |
|
129 |
value (i.e '?bulk=1'), the output contains detailed |
|
130 |
information about nodes as a list. |
|
131 |
|
|
132 |
Example: [ |
|
133 |
{ |
|
134 |
"pinst_cnt": 1, |
|
135 |
"mfree": 31280, |
|
136 |
"mtotal": 32763, |
|
137 |
"name": "www.example.com", |
|
138 |
"tags": [], |
|
139 |
"mnode": 512, |
|
140 |
"dtotal": 5246208, |
|
141 |
"sinst_cnt": 2, |
|
142 |
"dfree": 5171712 |
|
143 |
}, |
|
144 |
... |
|
145 |
] |
|
146 |
|
|
147 |
""" |
|
148 |
op = ganeti.opcodes.OpQueryNodes(output_fields=["name"], names=[]) |
|
149 |
nodeslist = baserlib.ExtractField(ganeti.cli.SubmitOpCode(op), 0) |
|
150 |
|
|
151 |
if 'bulk' in self.queryargs: |
|
152 |
return self._GetDetails(nodeslist) |
|
153 |
|
|
154 |
return baserlib.BuildUriList(nodeslist, "/nodes/%s", uri_fields=("id", "uri")) |
b/test/ganeti.rapi.resources_unittest.py | ||
---|---|---|
19 | 19 |
# 02110-1301, USA. |
20 | 20 |
|
21 | 21 |
|
22 |
"""Script for unittesting the rapi.resources module"""
|
|
22 |
"""Script for unittesting the RAPI resources module"""
|
|
23 | 23 |
|
24 | 24 |
|
25 | 25 |
import os |
... | ... | |
28 | 28 |
import time |
29 | 29 |
|
30 | 30 |
from ganeti import errors |
31 |
from ganeti.rapi import connector |
|
31 | 32 |
from ganeti.rapi import httperror |
32 |
from ganeti.rapi import resources |
|
33 | 33 |
from ganeti.rapi import RESTHTTPServer |
34 |
from ganeti.rapi import rlib1 |
|
34 | 35 |
|
35 | 36 |
|
36 | 37 |
class MapperTests(unittest.TestCase): |
37 | 38 |
"""Tests for remote API URI mapper.""" |
38 | 39 |
|
39 | 40 |
def setUp(self): |
40 |
self.map = resources.Mapper()
|
|
41 |
self.map = connector.Mapper()
|
|
41 | 42 |
|
42 | 43 |
def _TestUri(self, uri, result): |
43 | 44 |
self.assertEquals(self.map.getController(uri), result) |
... | ... | |
46 | 47 |
self.failUnlessRaises(httperror.HTTPNotFound, self.map.getController, uri) |
47 | 48 |
|
48 | 49 |
def testMapper(self): |
49 |
"""Testing resources.Mapper"""
|
|
50 |
"""Testing Mapper""" |
|
50 | 51 |
|
51 |
self._TestUri("/tags", (resources.R_tags, [], {})) |
|
52 |
self._TestUri("/tags", (rlib1.R_tags, [], {})) |
|
53 |
self._TestUri("/instances", (rlib1.R_instances, [], {})) |
|
52 | 54 |
|
53 | 55 |
self._TestUri('/instances/www.test.com', |
54 |
(resources.R_instances_name,
|
|
56 |
(rlib1.R_instances_name,
|
|
55 | 57 |
['www.test.com'], |
56 | 58 |
{})) |
57 | 59 |
|
58 | 60 |
self._TestUri('/instances/www.test.com/tags?f=5&f=6&alt=html', |
59 |
(resources.R_instances_name_tags,
|
|
61 |
(rlib1.R_instances_name_tags,
|
|
60 | 62 |
['www.test.com'], |
61 | 63 |
{'alt': ['html'], |
62 | 64 |
'f': ['5', '6'], |
... | ... | |
70 | 72 |
"""Testing for R_root class.""" |
71 | 73 |
|
72 | 74 |
def setUp(self): |
73 |
self.root = resources.R_root(None, None, None)
|
|
75 |
self.root = connector.R_root(None, None, None)
|
|
74 | 76 |
|
75 | 77 |
def testGet(self): |
76 | 78 |
expected = [ |
Also available in: Unified diff