Revision 441e7cfd
b/daemons/ganeti-rapi | ||
---|---|---|
22 | 22 |
""" |
23 | 23 |
|
24 | 24 |
import glob |
25 |
import logging |
|
25 | 26 |
import optparse |
26 | 27 |
import sys |
27 | 28 |
import os |
28 | 29 |
import signal |
29 | 30 |
|
31 |
from ganeti import logger |
|
30 | 32 |
from ganeti import constants |
31 | 33 |
from ganeti import errors |
32 | 34 |
from ganeti import http |
33 |
from ganeti import rpc |
|
34 | 35 |
from ganeti import ssconf |
35 | 36 |
from ganeti import utils |
36 | 37 |
from ganeti.rapi import connector |
... | ... | |
49 | 50 |
|
50 | 51 |
""" |
51 | 52 |
(HandlerClass, items, args) = self._resmap.getController(self.path) |
52 |
handler = HandlerClass(self, items, args) |
|
53 |
handler = HandlerClass(self, items, args, self.post_data)
|
|
53 | 54 |
|
54 | 55 |
command = self.command.upper() |
55 | 56 |
try: |
... | ... | |
58 | 59 |
raise http.HTTPBadRequest() |
59 | 60 |
|
60 | 61 |
try: |
61 |
result = fn() |
|
62 |
try: |
|
63 |
result = fn() |
|
64 |
except: |
|
65 |
logging.exception("Error while handling the %s request", command) |
|
66 |
raise |
|
62 | 67 |
|
63 | 68 |
except errors.OpPrereqError, err: |
64 | 69 |
# TODO: "Not found" is not always the correct error. Ganeti's core must |
... | ... | |
135 | 140 |
if options.fork: |
136 | 141 |
utils.Daemonize(logfile=constants.LOG_RAPISERVER) |
137 | 142 |
|
143 |
logger.SetupLogging(constants.LOG_RAPISERVER, debug=options.debug, |
|
144 |
stderr_logging=not options.fork) |
|
145 |
|
|
138 | 146 |
utils.WritePidFile(constants.RAPI_PID) |
139 | 147 |
|
140 | 148 |
log_fd = open(constants.LOG_RAPIACCESS, 'a') |
... | ... | |
155 | 163 |
|
156 | 164 |
|
157 | 165 |
if __name__ == '__main__': |
166 |
|
|
158 | 167 |
main() |
b/lib/rapi/baserlib.py | ||
---|---|---|
26 | 26 |
import ganeti.cli |
27 | 27 |
import ganeti.opcodes |
28 | 28 |
|
29 |
from ganeti import luxi |
|
30 |
|
|
29 | 31 |
|
30 | 32 |
def BuildUriList(ids, uri_format, uri_fields=("name", "uri")): |
31 | 33 |
"""Builds a URI list as used by index resources. |
... | ... | |
89 | 91 |
return list(tags) |
90 | 92 |
|
91 | 93 |
|
94 |
def _Tags_POST(kind, tags, name=None): |
|
95 |
"""Helper function to set tags. |
|
96 |
|
|
97 |
""" |
|
98 |
if name is None: |
|
99 |
# Do not cause "missing parameter" error, which happens if a parameter |
|
100 |
# is None. |
|
101 |
name = "" |
|
102 |
cl = luxi.Client() |
|
103 |
return cl.SubmitJob([ganeti.opcodes.OpAddTags(kind=kind, name=name, |
|
104 |
tags=tags)]) |
|
105 |
|
|
106 |
|
|
92 | 107 |
def MapBulkFields(itemslist, fields): |
93 | 108 |
"""Map value to field name in to one dictionary. |
94 | 109 |
|
... | ... | |
110 | 125 |
"""Generic class for resources. |
111 | 126 |
|
112 | 127 |
""" |
113 |
def __init__(self, request, items, queryargs): |
|
128 |
def __init__(self, request, items, queryargs, post_data):
|
|
114 | 129 |
"""Generic resource constructor. |
115 | 130 |
|
116 | 131 |
Args: |
... | ... | |
122 | 137 |
self.request = request |
123 | 138 |
self.items = items |
124 | 139 |
self.queryargs = queryargs |
140 |
self.post_data = post_data |
b/lib/rapi/connector.py | ||
---|---|---|
137 | 137 |
|
138 | 138 |
"/2/jobs": rlib2.R_2_jobs, |
139 | 139 |
"/2/nodes": rlib2.R_2_nodes, |
140 |
"/2/instances": rlib2.R_2_instances, |
|
141 |
re.compile(r'^/2/instances/([\w\._-]+)$'): rlib1.R_instances_name, |
|
142 |
re.compile(r'^/2/instances/([\w\._-]+)/tags$'): rlib2.R_2_instances_name_tags, |
|
140 | 143 |
re.compile(r'/2/jobs/(%s)$' % constants.JOB_ID_TEMPLATE): rlib2.R_2_jobs_id, |
141 | 144 |
}) |
b/lib/rapi/rlib2.py | ||
---|---|---|
129 | 129 |
result = ganeti.cli.SubmitOpCode(op) |
130 | 130 |
return baserlib.MapBulkFields(result, N_FIELDS) |
131 | 131 |
|
132 |
return baserlib.BuildUriList(nodeslist, "/nodes/%s", |
|
132 |
return baserlib.BuildUriList(nodeslist, "/2/nodes/%s",
|
|
133 | 133 |
uri_fields=("id", "uri")) |
134 |
|
|
135 |
|
|
136 |
class R_2_instances(baserlib.R_Generic): |
|
137 |
"""/2/instances resource. |
|
138 |
|
|
139 |
""" |
|
140 |
DOC_URI = "/2/instances" |
|
141 |
|
|
142 |
|
|
143 |
def GET(self): |
|
144 |
"""Returns a list of all available instances. |
|
145 |
|
|
146 |
Returns: |
|
147 |
A dictionary with 'name' and 'uri' keys for each of them. |
|
148 |
|
|
149 |
Example: [ |
|
150 |
{ |
|
151 |
"name": "web.example.com", |
|
152 |
"uri": "\/instances\/web.example.com" |
|
153 |
}, |
|
154 |
{ |
|
155 |
"name": "mail.example.com", |
|
156 |
"uri": "\/instances\/mail.example.com" |
|
157 |
}] |
|
158 |
|
|
159 |
If the optional 'bulk' argument is provided and set to 'true' |
|
160 |
value (i.e '?bulk=1'), the output contains detailed |
|
161 |
information about instances as a list. |
|
162 |
|
|
163 |
Example: [ |
|
164 |
{ |
|
165 |
"status": "running", |
|
166 |
"bridge": "xen-br0", |
|
167 |
"name": "web.example.com", |
|
168 |
"tags": ["tag1", "tag2"], |
|
169 |
"admin_ram": 512, |
|
170 |
"sda_size": 20480, |
|
171 |
"pnode": "node1.example.com", |
|
172 |
"mac": "01:23:45:67:89:01", |
|
173 |
"sdb_size": 4096, |
|
174 |
"snodes": ["node2.example.com"], |
|
175 |
"disk_template": "drbd", |
|
176 |
"ip": null, |
|
177 |
"admin_state": true, |
|
178 |
"os": "debian-etch", |
|
179 |
"vcpus": 2, |
|
180 |
"oper_state": true |
|
181 |
}, |
|
182 |
... |
|
183 |
] |
|
184 |
|
|
185 |
""" |
|
186 |
op = ganeti.opcodes.OpQueryInstances(output_fields=["name"], names=[]) |
|
187 |
instanceslist = baserlib.ExtractField(ganeti.cli.SubmitOpCode(op), 0) |
|
188 |
|
|
189 |
if 'bulk' in self.queryargs: |
|
190 |
op = ganeti.opcodes.OpQueryInstances(output_fields=I_FIELDS, |
|
191 |
names=instanceslist) |
|
192 |
result = ganeti.cli.SubmitOpCode(op) |
|
193 |
return baserlib.MapBulkFields(result, I_FIELDS) |
|
194 |
|
|
195 |
|
|
196 |
else: |
|
197 |
return baserlib.BuildUriList(instanceslist, "/2/instances/%s", |
|
198 |
uri_fields=("id", "uri")) |
|
199 |
|
|
200 |
|
|
201 |
class R_2_instances_name_tags(baserlib.R_Generic): |
|
202 |
"""/2/instances/[instance_name]/tags resource. |
|
203 |
|
|
204 |
Manages per-instance tags. |
|
205 |
|
|
206 |
""" |
|
207 |
DOC_URI = "/2/instances/[instance_name]/tags" |
|
208 |
|
|
209 |
def GET(self): |
|
210 |
"""Returns a list of instance tags. |
|
211 |
|
|
212 |
Example: ["tag1", "tag2", "tag3"] |
|
213 |
|
|
214 |
""" |
|
215 |
return baserlib._Tags_GET(constants.TAG_INSTANCE, name=self.items[0]) |
|
216 |
|
|
217 |
def POST(self): |
|
218 |
"""Add a set of tags to the instance. |
|
219 |
|
|
220 |
The reqest as a list of strings should be POST to this URI. And you'll have |
|
221 |
back a job id. |
|
222 |
|
|
223 |
""" |
|
224 |
return baserlib._Tags_POST(constants.TAG_INSTANCE, |
|
225 |
self.post_data, name=self.items[0]) |
b/test/ganeti.rapi.resources_unittest.py | ||
---|---|---|
28 | 28 |
from ganeti import errors |
29 | 29 |
from ganeti import http |
30 | 30 |
|
31 |
from ganeti.rapi import connector |
|
32 |
from ganeti.rapi import rlib1 |
|
31 |
from ganeti.rapi import connector
|
|
32 |
from ganeti.rapi import rlib1
|
|
33 | 33 |
|
34 | 34 |
|
35 | 35 |
class MapperTests(unittest.TestCase): |
... | ... | |
70 | 70 |
"""Testing for R_root class.""" |
71 | 71 |
|
72 | 72 |
def setUp(self): |
73 |
self.root = connector.R_root(None, None, None) |
|
73 |
self.root = connector.R_root(None, None, None, None)
|
|
74 | 74 |
|
75 | 75 |
def testGet(self): |
76 | 76 |
expected = [ |
Also available in: Unified diff