Revision 9189c902
b/doc/design-query2.rst | ||
---|---|---|
57 | 57 |
subclasses of ``objects.ConfigObject``, providing existing facilities |
58 | 58 |
for de-/serializing. |
59 | 59 |
|
60 |
.. _query-request: |
|
61 |
|
|
62 |
Query request |
|
63 |
+++++++++++++ |
|
64 |
|
|
65 |
Each query operation will take a single parameter, a query request |
|
66 |
dictionary with the following properties: |
|
67 |
|
|
68 |
``kind`` |
|
69 |
Denotes request kind. One of the following strings: |
|
70 |
|
|
71 |
``query`` |
|
72 |
Execute a query on items, optionally filtered (see below). For a |
|
73 |
description of the result see :ref:`query-result`. |
|
74 |
``fields`` |
|
75 |
Return list of supported fields as :ref:`field definitions |
|
76 |
<field-def>`. For a complete description of the result see |
|
77 |
:ref:`fields-result`. |
|
78 |
|
|
79 |
``filter`` |
|
60 |
Regular expressions |
|
61 |
+++++++++++++++++++ |
|
62 |
|
|
63 |
As it turned out, only very few fields for instances used regular |
|
64 |
expressions, all of which can easily be turned into static field names. |
|
65 |
Therefore their use in field names is dropped. Reasons: |
|
66 |
|
|
67 |
- When regexps are used and a field name is not listed as a simple |
|
68 |
string in the field dictionary, all keys in the field dictionary have |
|
69 |
to be checked whether they're a regular expression object and if so, |
|
70 |
matched (see ``utils.FindMatch``). |
|
71 |
- Code becomes simpler. There would be no need anymore to care about |
|
72 |
regular expressions as field names—they'd all be simple strings, even |
|
73 |
if there are many more. The list of field names would be static once |
|
74 |
built at module-load time. |
|
75 |
- There's the issue of formatting titles for the clients. Should it be |
|
76 |
done in the server? In the client? The field definition's title would |
|
77 |
contain backreferences to the regexp groups in the field name |
|
78 |
(``re.MatchObject.expand`` can be used). With just strings, the field |
|
79 |
definitions can be passed directly to the client. They're static. |
|
80 |
- Only a side note: In the memory consumed for 1'000 |
|
81 |
``_sre.SRE_Pattern`` objects (as returned by ``re.compile`` for an |
|
82 |
expression with one group) one can easily store 10'000 strings of the |
|
83 |
same length (the regexp objects keep the expression string around, so |
|
84 |
compiling the expression always uses more memory). |
|
85 |
|
|
86 |
|
|
87 |
.. _item-types: |
|
88 |
|
|
89 |
Item types |
|
90 |
++++++++++ |
|
91 |
|
|
92 |
The proposal is to implement this new interface for the following |
|
93 |
items: |
|
94 |
|
|
95 |
``instance`` |
|
96 |
Instances |
|
97 |
``node`` |
|
98 |
Nodes |
|
99 |
``job`` |
|
100 |
Jobs |
|
101 |
``lock`` |
|
102 |
Locks |
|
103 |
|
|
104 |
.. _data-query: |
|
105 |
|
|
106 |
Data query |
|
107 |
++++++++++ |
|
108 |
|
|
109 |
.. _data-query-request: |
|
110 |
|
|
111 |
Request |
|
112 |
^^^^^^^ |
|
113 |
|
|
114 |
The request is a dictionary with the following entries: |
|
115 |
|
|
116 |
``kind`` (string, required) |
|
117 |
An :ref:`item type <item-types>`. |
|
118 |
``fields`` (list of strings, required) |
|
119 |
List of names of fields to return. Example:: |
|
120 |
|
|
121 |
["name", "mem", "nic0.ip", "disk0.size", "disk1.size"] |
|
122 |
|
|
123 |
``filter`` (optional) |
|
80 | 124 |
This will be used to filter queries. In this implementation only names |
81 | 125 |
can be filtered to replace the previous ``names`` parameter to |
82 | 126 |
queries. An empty filter (``None``) will return all items. To retrieve |
... | ... | |
98 | 142 |
but disabled in the master daemon, will be dropped. Direct calls to |
99 | 143 |
opcodes have to be used instead. |
100 | 144 |
|
101 |
.. _query-result:
|
|
145 |
.. _data-query-response:
|
|
102 | 146 |
|
103 |
New query result format
|
|
104 |
+++++++++++++++++++++++
|
|
147 |
Response
|
|
148 |
^^^^^^^^
|
|
105 | 149 |
|
106 | 150 |
The result is a dictionary with the following entries: |
107 | 151 |
|
108 |
``fields`` |
|
152 |
``fields`` (list of :ref:`field definitions <field-def>`)
|
|
109 | 153 |
In-order list of a :ref:`field definition <field-def>` for each |
110 | 154 |
requested field, unknown fields are returned with the kind |
111 | 155 |
``unknown``. Length must be equal to number of requested fields. |
112 |
``data`` |
|
113 |
A list of lists, one list for each item found. Each item's list must
|
|
114 |
have one entry for each field listed in ``fields``. Each field entry
|
|
115 |
is a tuple of ``(status, value)``. ``status`` must be one of the
|
|
116 |
following values: |
|
156 |
``data`` (list of lists of tuples)
|
|
157 |
List of lists, one list for each item found. Each item's list must
|
|
158 |
have one entry for each field listed in ``fields`` (meaning their
|
|
159 |
length is equal). Each field entry is a tuple of ``(status, value)``.
|
|
160 |
``status`` must be one of the following values:
|
|
117 | 161 |
|
118 | 162 |
Normal (numeric 0) |
119 | 163 |
Value is available and matches the kind in the :ref:`field |
120 | 164 |
definition <field-def>`. |
121 |
Value unavailable (numeric 1) |
|
165 |
Unknown field (numeric 1) |
|
166 |
Field for this column is not known. Value must be ``None``. |
|
167 |
No data (numeric 2) |
|
122 | 168 |
Exact meaning depends on query, e.g. node is unreachable or marked |
123 | 169 |
offline. Value must be ``None``. |
124 |
Unknown field (numeric 2) |
|
125 |
Field for this column is not known. Value must be ``None``. |
|
170 |
Value unavailable for item (numeric 3) |
|
171 |
Used if, for example, NIC 3 is requested for an instance with only |
|
172 |
one network interface. Value must be ``None``. |
|
126 | 173 |
|
127 |
Example:: |
|
174 |
Example response after requesting the fields ``name``, ``mfree``, |
|
175 |
``xyz``, ``mtotal``, ``nic0.ip``, ``nic1.ip`` and ``nic2.ip``:: |
|
128 | 176 |
|
129 | 177 |
{ |
130 | 178 |
"fields": [ |
... | ... | |
133 | 181 |
# Unknown field |
134 | 182 |
{ "name": "xyz", "title": None, "kind": "unknown", }, |
135 | 183 |
{ "name": "mtotal", "title": "MemTotal", "kind": "unit", }, |
184 |
{ "name": "nic0.ip", "title": "Nic.IP/0", "kind": "text", }, |
|
185 |
{ "name": "nic1.ip", "title": "Nic.IP/1", "kind": "text", }, |
|
186 |
{ "name": "nic2.ip", "title": "Nic.IP/2", "kind": "text", }, |
|
136 | 187 |
], |
137 | 188 |
|
138 | 189 |
"data": [ |
139 |
[(0, "node1"), (0, 128), (2, None), (0, 4096)], |
|
140 |
# Node not available |
|
141 |
[(0, "node2"), (1, None), (2, None), (1, None)], |
|
190 |
[(0, "node1"), (0, 128), (1, None), (0, 4096), |
|
191 |
(0, "192.0.2.1"), (0, "192.0.2.2"), (3, None)], |
|
192 |
[(0, "node2"), (0, 96), (1, None), (0, 5000), |
|
193 |
(0, "192.0.2.21"), (0, "192.0.2.39"), (3, "192.0.2.90")], |
|
194 |
# Node not available, can't get "mfree" or "mtotal" |
|
195 |
[(0, "node3"), (2, None), (1, None), (2, None), |
|
196 |
(0, "192.0.2.30"), (3, None), (3, None)], |
|
142 | 197 |
], |
143 | 198 |
} |
144 | 199 |
|
145 |
.. _fields-result: |
|
200 |
.. _fields-query: |
|
201 |
|
|
202 |
Fields query |
|
203 |
++++++++++++ |
|
204 |
|
|
205 |
.. _fields-query-request: |
|
206 |
|
|
207 |
Request |
|
208 |
^^^^^^^ |
|
209 |
|
|
210 |
The request is a dictionary with the following entries: |
|
211 |
|
|
212 |
``kind`` (string, required) |
|
213 |
An :ref:`item type <item-types>`. |
|
214 |
``fields`` (list of strings, optional) |
|
215 |
List of names of fields to return. If not set, all fields are |
|
216 |
returned. Example:: |
|
217 |
|
|
218 |
["name", "mem", "nic0.ip", "disk0.size", "disk1.size"] |
|
146 | 219 |
|
147 |
Field query result format |
|
148 |
+++++++++++++++++++++++++ |
|
220 |
.. _fields-query-response: |
|
221 |
|
|
222 |
Response |
|
223 |
^^^^^^^^ |
|
149 | 224 |
|
150 | 225 |
The result is a dictionary with the following entries: |
151 | 226 |
|
152 |
``fields`` |
|
153 |
List of :ref:`field definitions <field-def>` for each available field. |
|
227 |
``fields`` (list of :ref:`field definitions <field-def>`) |
|
228 |
List of a :ref:`field definition <field-def>` for each field. If |
|
229 |
``fields`` was set in the request and contained an unknown field, it |
|
230 |
is returned as type ``unknown``. |
|
154 | 231 |
|
155 | 232 |
Example:: |
156 | 233 |
|
... | ... | |
159 | 236 |
{ "name": "name", "title": "Name", "kind": "text", }, |
160 | 237 |
{ "name": "mfree", "title": "MemFree", "kind": "unit", }, |
161 | 238 |
{ "name": "mtotal", "title": "MemTotal", "kind": "unit", }, |
239 |
{ "name": "nic0.ip", "title": "Nic.IP/0", "kind": "text", }, |
|
240 |
{ "name": "nic1.ip", "title": "Nic.IP/1", "kind": "text", }, |
|
241 |
{ "name": "nic2.ip", "title": "Nic.IP/2", "kind": "text", }, |
|
242 |
{ "name": "nic3.ip", "title": "Nic.IP/3", "kind": "text", }, |
|
243 |
# … |
|
244 |
{ "name": "disk0.size", "title": "Disk.Size/0", "kind": "unit", }, |
|
245 |
{ "name": "disk1.size", "title": "Disk.Size/1", "kind": "unit", }, |
|
246 |
{ "name": "disk2.size", "title": "Disk.Size/2", "kind": "unit", }, |
|
247 |
{ "name": "disk3.size", "title": "Disk.Size/3", "kind": "unit", }, |
|
248 |
# … |
|
162 | 249 |
] |
163 | 250 |
} |
164 | 251 |
|
... | ... | |
169 | 256 |
|
170 | 257 |
A field definition is a dictionary with the following entries: |
171 | 258 |
|
172 |
``name`` |
|
173 |
The field name as a regular expression. The latter is necessary to |
|
174 |
represent dynamic fields (e.g. NIC 3 of an instance). |
|
175 |
``title`` |
|
259 |
``name`` (string) |
|
260 |
Field name. Must only contain characters matching ``[a-z0-9/._]``. |
|
261 |
``title`` (string) |
|
176 | 262 |
Human-readable title to use in output. Must not contain whitespace. |
177 |
``kind`` |
|
263 |
``kind`` (string)
|
|
178 | 264 |
Field type, one of the following: |
179 | 265 |
|
180 |
.. TODO: Investigate whether there are fields with floating point |
|
181 |
.. numbers |
|
182 |
|
|
183 | 266 |
``unknown`` |
184 |
Unknown field (only used for :ref:`data queries <query-request>`)
|
|
267 |
Unknown field |
|
185 | 268 |
``text`` |
186 | 269 |
String |
187 | 270 |
``bool`` |
... | ... | |
190 | 273 |
Numeric |
191 | 274 |
``unit`` |
192 | 275 |
Numeric, in megabytes |
276 |
``timestamp`` |
|
277 |
Unix timestamp in seconds since the epoch |
|
193 | 278 |
``other`` |
194 | 279 |
Free-form type, depending on query |
195 | 280 |
|
... | ... | |
197 | 282 |
formatting any unknown types the same way as "other", which should be |
198 | 283 |
a string representation in most cases. |
199 | 284 |
|
285 |
.. TODO: Investigate whether there are fields with floating point |
|
286 |
.. numbers |
|
287 |
|
|
200 | 288 |
Example 1 (item name):: |
201 | 289 |
|
202 | 290 |
{ |
... | ... | |
227 | 315 |
+++++++++++++++++ |
228 | 316 |
|
229 | 317 |
To limit the amount of code necessary, the :ref:`new result format |
230 |
<query-result>` will be converted for older clients. Unavailable values |
|
231 |
are set to ``None``. If unknown fields were requested, the whole query |
|
232 |
fails as the client expects exactly the fields it requested. |
|
318 |
<data-query-response>` will be converted for clients calling the old |
|
319 |
methods. Unavailable values are set to ``None``. If unknown fields were |
|
320 |
requested, the whole query fails as the client expects exactly the |
|
321 |
fields it requested. |
|
322 |
|
|
323 |
.. _luxi: |
|
233 | 324 |
|
234 | 325 |
LUXI |
235 | 326 |
++++ |
236 | 327 |
|
237 | 328 |
Currently query calls take a number of parameters, e.g. names, fields |
238 | 329 |
and whether to use locking. These will continue to work and return the |
239 |
:ref:`old result format <old-result-format>`. To use the new query |
|
240 |
requests, the same calls must be invoked with a single parameter as the |
|
241 |
:ref:`query object <query-request>`. Only clients using the new call |
|
242 |
syntax will be able to make use of new features such as filters. |
|
330 |
:ref:`old result format <old-result-format>`. Only clients using the |
|
331 |
new calls described below will be able to make use of new features such |
|
332 |
as filters. Two new calls are introduced: |
|
333 |
|
|
334 |
``Query`` |
|
335 |
Execute a query on items, optionally filtered. Takes a single |
|
336 |
parameter, a :ref:`query object <data-query-request>` encoded as a |
|
337 |
dictionary and returns a :ref:`data query response |
|
338 |
<data-query-response`. |
|
339 |
``QueryFields`` |
|
340 |
Return list of supported fields as :ref:`field definitions |
|
341 |
<field-def>`. Takes a single parameter, a :ref:`fields query object |
|
342 |
<fields-query-request>` encoded as a dictionary and returns a |
|
343 |
:ref:`fields query response <fields-query-response>`. |
|
344 |
|
|
243 | 345 |
|
244 | 346 |
Python |
245 | 347 |
++++++ |
... | ... | |
258 | 360 |
proposal directly and to receive the unmodified result. The new formats |
259 | 361 |
are a lot more verbose, flexible and extensible. |
260 | 362 |
|
363 |
.. _cli-programs: |
|
364 |
|
|
365 |
CLI programs |
|
366 |
++++++++++++ |
|
367 |
|
|
368 |
Command line programs might have difficulties to display the verbose |
|
369 |
status data to the user. There are several options: |
|
370 |
|
|
371 |
- Use colours to indicate missing values |
|
372 |
- Display status as value in parentheses, e.g. "(unavailable)" |
|
373 |
- Hide unknown columns from the result table and print a warning |
|
374 |
- Exit with non-zero code to indicate failures and/or missing data |
|
375 |
|
|
376 |
Some are better for interactive usage, some better for use by other |
|
377 |
programs. It is expected that a combination will be used. The column |
|
378 |
separator (``--separator=…``) can be used to differentiate between |
|
379 |
interactive and programmatic usage. |
|
380 |
|
|
261 | 381 |
|
262 | 382 |
Other discussed solutions |
263 | 383 |
------------------------- |
Also available in: Unified diff