Statistics
| Branch: | Tag: | Revision:

root / lib / query.py @ 6a2c20f2

History | View | Annotate | Download (82.1 kB)

1
#
2
#
3

    
4
# Copyright (C) 2010, 2011, 2012 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
"""Module for query operations
23

24
How it works:
25

26
  - Add field definitions
27
    - See how L{NODE_FIELDS} is built
28
    - Each field gets:
29
      - Query field definition (L{objects.QueryFieldDefinition}, use
30
        L{_MakeField} for creating), containing:
31
          - Name, must be lowercase and match L{FIELD_NAME_RE}
32
          - Title for tables, must not contain whitespace and match
33
            L{TITLE_RE}
34
          - Value data type, e.g. L{constants.QFT_NUMBER}
35
          - Human-readable description, must not end with punctuation or
36
            contain newlines
37
      - Data request type, see e.g. C{NQ_*}
38
      - OR-ed flags, see C{QFF_*}
39
      - A retrieval function, see L{Query.__init__} for description
40
    - Pass list of fields through L{_PrepareFieldList} for preparation and
41
      checks
42
  - Instantiate L{Query} with prepared field list definition and selected fields
43
  - Call L{Query.RequestedData} to determine what data to collect/compute
44
  - Call L{Query.Query} or L{Query.OldStyleQuery} with collected data and use
45
    result
46
      - Data container must support iteration using C{__iter__}
47
      - Items are passed to retrieval functions and can have any format
48
  - Call L{Query.GetFields} to get list of definitions for selected fields
49

50
@attention: Retrieval functions must be idempotent. They can be called multiple
51
  times, in any order and any number of times.
52

53
"""
54

    
55
import logging
56
import operator
57
import re
58

    
59
from ganeti import constants
60
from ganeti import errors
61
from ganeti import utils
62
from ganeti import compat
63
from ganeti import objects
64
from ganeti import ht
65
from ganeti import runtime
66
from ganeti import qlang
67
from ganeti import jstore
68

    
69
from ganeti.constants import (QFT_UNKNOWN, QFT_TEXT, QFT_BOOL, QFT_NUMBER,
70
                              QFT_UNIT, QFT_TIMESTAMP, QFT_OTHER,
71
                              RS_NORMAL, RS_UNKNOWN, RS_NODATA,
72
                              RS_UNAVAIL, RS_OFFLINE)
73

    
74
(NETQ_CONFIG,
75
 NETQ_GROUP,
76
 NETQ_STATS,
77
 NETQ_INST) = range(300, 304)
78

    
79
# Constants for requesting data from the caller/data provider. Each property
80
# collected/computed separately by the data provider should have its own to
81
# only collect the requested data and not more.
82

    
83
(NQ_CONFIG,
84
 NQ_INST,
85
 NQ_LIVE,
86
 NQ_GROUP,
87
 NQ_OOB) = range(1, 6)
88

    
89
(IQ_CONFIG,
90
 IQ_LIVE,
91
 IQ_DISKUSAGE,
92
 IQ_CONSOLE,
93
 IQ_NODES,
94
 IQ_NETWORKS) = range(100, 106)
95

    
96
(LQ_MODE,
97
 LQ_OWNER,
98
 LQ_PENDING) = range(10, 13)
99

    
100
(GQ_CONFIG,
101
 GQ_NODE,
102
 GQ_INST,
103
 GQ_DISKPARAMS) = range(200, 204)
104

    
105
(CQ_CONFIG,
106
 CQ_QUEUE_DRAINED,
107
 CQ_WATCHER_PAUSE) = range(300, 303)
108

    
109
(JQ_ARCHIVED, ) = range(400, 401)
110

    
111
# Query field flags
112
QFF_HOSTNAME = 0x01
113
QFF_IP_ADDRESS = 0x02
114
QFF_JOB_ID = 0x04
115
QFF_SPLIT_TIMESTAMP = 0x08
116
# Next values: 0x10, 0x20, 0x40, 0x80, 0x100, 0x200
117
QFF_ALL = (QFF_HOSTNAME | QFF_IP_ADDRESS | QFF_JOB_ID | QFF_SPLIT_TIMESTAMP)
118

    
119
FIELD_NAME_RE = re.compile(r"^[a-z0-9/._]+$")
120
TITLE_RE = re.compile(r"^[^\s]+$")
121
DOC_RE = re.compile(r"^[A-Z].*[^.,?!]$")
122

    
123
#: Verification function for each field type
124
_VERIFY_FN = {
125
  QFT_UNKNOWN: ht.TNone,
126
  QFT_TEXT: ht.TString,
127
  QFT_BOOL: ht.TBool,
128
  QFT_NUMBER: ht.TInt,
129
  QFT_UNIT: ht.TInt,
130
  QFT_TIMESTAMP: ht.TNumber,
131
  QFT_OTHER: lambda _: True,
132
  }
133

    
134
# Unique objects for special field statuses
135
_FS_UNKNOWN = object()
136
_FS_NODATA = object()
137
_FS_UNAVAIL = object()
138
_FS_OFFLINE = object()
139

    
140
#: List of all special status
141
_FS_ALL = compat.UniqueFrozenset([
142
  _FS_UNKNOWN,
143
  _FS_NODATA,
144
  _FS_UNAVAIL,
145
  _FS_OFFLINE,
146
  ])
147

    
148
#: VType to QFT mapping
149
_VTToQFT = {
150
  # TODO: fix validation of empty strings
151
  constants.VTYPE_STRING: QFT_OTHER, # since VTYPE_STRINGs can be empty
152
  constants.VTYPE_MAYBE_STRING: QFT_OTHER,
153
  constants.VTYPE_BOOL: QFT_BOOL,
154
  constants.VTYPE_SIZE: QFT_UNIT,
155
  constants.VTYPE_INT: QFT_NUMBER,
156
  }
157

    
158
_SERIAL_NO_DOC = "%s object serial number, incremented on each modification"
159

    
160

    
161
def _GetUnknownField(ctx, item): # pylint: disable=W0613
162
  """Gets the contents of an unknown field.
163

164
  """
165
  return _FS_UNKNOWN
166

    
167

    
168
def _GetQueryFields(fielddefs, selected):
169
  """Calculates the internal list of selected fields.
170

171
  Unknown fields are returned as L{constants.QFT_UNKNOWN}.
172

173
  @type fielddefs: dict
174
  @param fielddefs: Field definitions
175
  @type selected: list of strings
176
  @param selected: List of selected fields
177

178
  """
179
  result = []
180

    
181
  for name in selected:
182
    try:
183
      fdef = fielddefs[name]
184
    except KeyError:
185
      fdef = (_MakeField(name, name, QFT_UNKNOWN, "Unknown field '%s'" % name),
186
              None, 0, _GetUnknownField)
187

    
188
    assert len(fdef) == 4
189

    
190
    result.append(fdef)
191

    
192
  return result
193

    
194

    
195
def GetAllFields(fielddefs):
196
  """Extract L{objects.QueryFieldDefinition} from field definitions.
197

198
  @rtype: list of L{objects.QueryFieldDefinition}
199

200
  """
201
  return [fdef for (fdef, _, _, _) in fielddefs]
202

    
203

    
204
class _FilterHints:
205
  """Class for filter analytics.
206

207
  When filters are used, the user of the L{Query} class usually doesn't know
208
  exactly which items will be necessary for building the result. It therefore
209
  has to prepare and compute the input data for potentially returning
210
  everything.
211

212
  There are two ways to optimize this. The first, and simpler, is to assign
213
  each field a group of data, so that the caller can determine which
214
  computations are necessary depending on the data groups requested. The list
215
  of referenced groups must also be computed for fields referenced in the
216
  filter.
217

218
  The second is restricting the items based on a primary key. The primary key
219
  is usually a unique name (e.g. a node name). This class extracts all
220
  referenced names from a filter. If it encounters any filter condition which
221
  disallows such a list to be determined (e.g. a non-equality filter), all
222
  names will be requested.
223

224
  The end-effect is that any operation other than L{qlang.OP_OR} and
225
  L{qlang.OP_EQUAL} will make the query more expensive.
226

227
  """
228
  def __init__(self, namefield):
229
    """Initializes this class.
230

231
    @type namefield: string
232
    @param namefield: Field caller is interested in
233

234
    """
235
    self._namefield = namefield
236

    
237
    #: Whether all names need to be requested (e.g. if a non-equality operator
238
    #: has been used)
239
    self._allnames = False
240

    
241
    #: Which names to request
242
    self._names = None
243

    
244
    #: Data kinds referenced by the filter (used by L{Query.RequestedData})
245
    self._datakinds = set()
246

    
247
  def RequestedNames(self):
248
    """Returns all requested values.
249

250
    Returns C{None} if list of values can't be determined (e.g. encountered
251
    non-equality operators).
252

253
    @rtype: list
254

255
    """
256
    if self._allnames or self._names is None:
257
      return None
258

    
259
    return utils.UniqueSequence(self._names)
260

    
261
  def ReferencedData(self):
262
    """Returns all kinds of data referenced by the filter.
263

264
    """
265
    return frozenset(self._datakinds)
266

    
267
  def _NeedAllNames(self):
268
    """Changes internal state to request all names.
269

270
    """
271
    self._allnames = True
272
    self._names = None
273

    
274
  def NoteLogicOp(self, op):
275
    """Called when handling a logic operation.
276

277
    @type op: string
278
    @param op: Operator
279

280
    """
281
    if op != qlang.OP_OR:
282
      self._NeedAllNames()
283

    
284
  def NoteUnaryOp(self, op, datakind): # pylint: disable=W0613
285
    """Called when handling an unary operation.
286

287
    @type op: string
288
    @param op: Operator
289

290
    """
291
    if datakind is not None:
292
      self._datakinds.add(datakind)
293

    
294
    self._NeedAllNames()
295

    
296
  def NoteBinaryOp(self, op, datakind, name, value):
297
    """Called when handling a binary operation.
298

299
    @type op: string
300
    @param op: Operator
301
    @type name: string
302
    @param name: Left-hand side of operator (field name)
303
    @param value: Right-hand side of operator
304

305
    """
306
    if datakind is not None:
307
      self._datakinds.add(datakind)
308

    
309
    if self._allnames:
310
      return
311

    
312
    # If any operator other than equality was used, all names need to be
313
    # retrieved
314
    if op == qlang.OP_EQUAL and name == self._namefield:
315
      if self._names is None:
316
        self._names = []
317
      self._names.append(value)
318
    else:
319
      self._NeedAllNames()
320

    
321

    
322
def _WrapLogicOp(op_fn, sentences, ctx, item):
323
  """Wrapper for logic operator functions.
324

325
  """
326
  return op_fn(fn(ctx, item) for fn in sentences)
327

    
328

    
329
def _WrapUnaryOp(op_fn, inner, ctx, item):
330
  """Wrapper for unary operator functions.
331

332
  """
333
  return op_fn(inner(ctx, item))
334

    
335

    
336
def _WrapBinaryOp(op_fn, retrieval_fn, value, ctx, item):
337
  """Wrapper for binary operator functions.
338

339
  """
340
  return op_fn(retrieval_fn(ctx, item), value)
341

    
342

    
343
def _WrapNot(fn, lhs, rhs):
344
  """Negates the result of a wrapped function.
345

346
  """
347
  return not fn(lhs, rhs)
348

    
349

    
350
def _PrepareRegex(pattern):
351
  """Compiles a regular expression.
352

353
  """
354
  try:
355
    return re.compile(pattern)
356
  except re.error, err:
357
    raise errors.ParameterError("Invalid regex pattern (%s)" % err)
358

    
359

    
360
def _PrepareSplitTimestamp(value):
361
  """Prepares a value for comparison by L{_MakeSplitTimestampComparison}.
362

363
  """
364
  if ht.TNumber(value):
365
    return value
366
  else:
367
    return utils.MergeTime(value)
368

    
369

    
370
def _MakeSplitTimestampComparison(fn):
371
  """Compares split timestamp values after converting to float.
372

373
  """
374
  return lambda lhs, rhs: fn(utils.MergeTime(lhs), rhs)
375

    
376

    
377
def _MakeComparisonChecks(fn):
378
  """Prepares flag-specific comparisons using a comparison function.
379

380
  """
381
  return [
382
    (QFF_SPLIT_TIMESTAMP, _MakeSplitTimestampComparison(fn),
383
     _PrepareSplitTimestamp),
384
    (QFF_JOB_ID, lambda lhs, rhs: fn(jstore.ParseJobId(lhs), rhs),
385
     jstore.ParseJobId),
386
    (None, fn, None),
387
    ]
388

    
389

    
390
class _FilterCompilerHelper:
391
  """Converts a query filter to a callable usable for filtering.
392

393
  """
394
  # String statement has no effect, pylint: disable=W0105
395

    
396
  #: How deep filters can be nested
397
  _LEVELS_MAX = 10
398

    
399
  # Unique identifiers for operator groups
400
  (_OPTYPE_LOGIC,
401
   _OPTYPE_UNARY,
402
   _OPTYPE_BINARY) = range(1, 4)
403

    
404
  """Functions for equality checks depending on field flags.
405

406
  List of tuples containing flags and a callable receiving the left- and
407
  right-hand side of the operator. The flags are an OR-ed value of C{QFF_*}
408
  (e.g. L{QFF_HOSTNAME} or L{QFF_SPLIT_TIMESTAMP}).
409

410
  Order matters. The first item with flags will be used. Flags are checked
411
  using binary AND.
412

413
  """
414
  _EQUALITY_CHECKS = [
415
    (QFF_HOSTNAME,
416
     lambda lhs, rhs: utils.MatchNameComponent(rhs, [lhs],
417
                                               case_sensitive=False),
418
     None),
419
    (QFF_SPLIT_TIMESTAMP, _MakeSplitTimestampComparison(operator.eq),
420
     _PrepareSplitTimestamp),
421
    (None, operator.eq, None),
422
    ]
423

    
424
  """Known operators
425

426
  Operator as key (C{qlang.OP_*}), value a tuple of operator group
427
  (C{_OPTYPE_*}) and a group-specific value:
428

429
    - C{_OPTYPE_LOGIC}: Callable taking any number of arguments; used by
430
      L{_HandleLogicOp}
431
    - C{_OPTYPE_UNARY}: Always C{None}; details handled by L{_HandleUnaryOp}
432
    - C{_OPTYPE_BINARY}: Callable taking exactly two parameters, the left- and
433
      right-hand side of the operator, used by L{_HandleBinaryOp}
434

435
  """
436
  _OPS = {
437
    # Logic operators
438
    qlang.OP_OR: (_OPTYPE_LOGIC, compat.any),
439
    qlang.OP_AND: (_OPTYPE_LOGIC, compat.all),
440

    
441
    # Unary operators
442
    qlang.OP_NOT: (_OPTYPE_UNARY, None),
443
    qlang.OP_TRUE: (_OPTYPE_UNARY, None),
444

    
445
    # Binary operators
446
    qlang.OP_EQUAL: (_OPTYPE_BINARY, _EQUALITY_CHECKS),
447
    qlang.OP_NOT_EQUAL:
448
      (_OPTYPE_BINARY, [(flags, compat.partial(_WrapNot, fn), valprepfn)
449
                        for (flags, fn, valprepfn) in _EQUALITY_CHECKS]),
450
    qlang.OP_LT: (_OPTYPE_BINARY, _MakeComparisonChecks(operator.lt)),
451
    qlang.OP_LE: (_OPTYPE_BINARY, _MakeComparisonChecks(operator.le)),
452
    qlang.OP_GT: (_OPTYPE_BINARY, _MakeComparisonChecks(operator.gt)),
453
    qlang.OP_GE: (_OPTYPE_BINARY, _MakeComparisonChecks(operator.ge)),
454
    qlang.OP_REGEXP: (_OPTYPE_BINARY, [
455
      (None, lambda lhs, rhs: rhs.search(lhs), _PrepareRegex),
456
      ]),
457
    qlang.OP_CONTAINS: (_OPTYPE_BINARY, [
458
      (None, operator.contains, None),
459
      ]),
460
    }
461

    
462
  def __init__(self, fields):
463
    """Initializes this class.
464

465
    @param fields: Field definitions (return value of L{_PrepareFieldList})
466

467
    """
468
    self._fields = fields
469
    self._hints = None
470
    self._op_handler = None
471

    
472
  def __call__(self, hints, qfilter):
473
    """Converts a query filter into a callable function.
474

475
    @type hints: L{_FilterHints} or None
476
    @param hints: Callbacks doing analysis on filter
477
    @type qfilter: list
478
    @param qfilter: Filter structure
479
    @rtype: callable
480
    @return: Function receiving context and item as parameters, returning
481
             boolean as to whether item matches filter
482

483
    """
484
    self._op_handler = {
485
      self._OPTYPE_LOGIC:
486
        (self._HandleLogicOp, getattr(hints, "NoteLogicOp", None)),
487
      self._OPTYPE_UNARY:
488
        (self._HandleUnaryOp, getattr(hints, "NoteUnaryOp", None)),
489
      self._OPTYPE_BINARY:
490
        (self._HandleBinaryOp, getattr(hints, "NoteBinaryOp", None)),
491
      }
492

    
493
    try:
494
      filter_fn = self._Compile(qfilter, 0)
495
    finally:
496
      self._op_handler = None
497

    
498
    return filter_fn
499

    
500
  def _Compile(self, qfilter, level):
501
    """Inner function for converting filters.
502

503
    Calls the correct handler functions for the top-level operator. This
504
    function is called recursively (e.g. for logic operators).
505

506
    """
507
    if not (isinstance(qfilter, (list, tuple)) and qfilter):
508
      raise errors.ParameterError("Invalid filter on level %s" % level)
509

    
510
    # Limit recursion
511
    if level >= self._LEVELS_MAX:
512
      raise errors.ParameterError("Only up to %s levels are allowed (filter"
513
                                  " nested too deep)" % self._LEVELS_MAX)
514

    
515
    # Create copy to be modified
516
    operands = qfilter[:]
517
    op = operands.pop(0)
518

    
519
    try:
520
      (kind, op_data) = self._OPS[op]
521
    except KeyError:
522
      raise errors.ParameterError("Unknown operator '%s'" % op)
523

    
524
    (handler, hints_cb) = self._op_handler[kind]
525

    
526
    return handler(hints_cb, level, op, op_data, operands)
527

    
528
  def _LookupField(self, name):
529
    """Returns a field definition by name.
530

531
    """
532
    try:
533
      return self._fields[name]
534
    except KeyError:
535
      raise errors.ParameterError("Unknown field '%s'" % name)
536

    
537
  def _HandleLogicOp(self, hints_fn, level, op, op_fn, operands):
538
    """Handles logic operators.
539

540
    @type hints_fn: callable
541
    @param hints_fn: Callback doing some analysis on the filter
542
    @type level: integer
543
    @param level: Current depth
544
    @type op: string
545
    @param op: Operator
546
    @type op_fn: callable
547
    @param op_fn: Function implementing operator
548
    @type operands: list
549
    @param operands: List of operands
550

551
    """
552
    if hints_fn:
553
      hints_fn(op)
554

    
555
    return compat.partial(_WrapLogicOp, op_fn,
556
                          [self._Compile(op, level + 1) for op in operands])
557

    
558
  def _HandleUnaryOp(self, hints_fn, level, op, op_fn, operands):
559
    """Handles unary operators.
560

561
    @type hints_fn: callable
562
    @param hints_fn: Callback doing some analysis on the filter
563
    @type level: integer
564
    @param level: Current depth
565
    @type op: string
566
    @param op: Operator
567
    @type op_fn: callable
568
    @param op_fn: Function implementing operator
569
    @type operands: list
570
    @param operands: List of operands
571

572
    """
573
    assert op_fn is None
574

    
575
    if len(operands) != 1:
576
      raise errors.ParameterError("Unary operator '%s' expects exactly one"
577
                                  " operand" % op)
578

    
579
    if op == qlang.OP_TRUE:
580
      (_, datakind, _, retrieval_fn) = self._LookupField(operands[0])
581

    
582
      if hints_fn:
583
        hints_fn(op, datakind)
584

    
585
      op_fn = operator.truth
586
      arg = retrieval_fn
587
    elif op == qlang.OP_NOT:
588
      if hints_fn:
589
        hints_fn(op, None)
590

    
591
      op_fn = operator.not_
592
      arg = self._Compile(operands[0], level + 1)
593
    else:
594
      raise errors.ProgrammerError("Can't handle operator '%s'" % op)
595

    
596
    return compat.partial(_WrapUnaryOp, op_fn, arg)
597

    
598
  def _HandleBinaryOp(self, hints_fn, level, op, op_data, operands):
599
    """Handles binary operators.
600

601
    @type hints_fn: callable
602
    @param hints_fn: Callback doing some analysis on the filter
603
    @type level: integer
604
    @param level: Current depth
605
    @type op: string
606
    @param op: Operator
607
    @param op_data: Functions implementing operators
608
    @type operands: list
609
    @param operands: List of operands
610

611
    """
612
    # Unused arguments, pylint: disable=W0613
613
    try:
614
      (name, value) = operands
615
    except (ValueError, TypeError):
616
      raise errors.ParameterError("Invalid binary operator, expected exactly"
617
                                  " two operands")
618

    
619
    (fdef, datakind, field_flags, retrieval_fn) = self._LookupField(name)
620

    
621
    assert fdef.kind != QFT_UNKNOWN
622

    
623
    # TODO: Type conversions?
624

    
625
    verify_fn = _VERIFY_FN[fdef.kind]
626
    if not verify_fn(value):
627
      raise errors.ParameterError("Unable to compare field '%s' (type '%s')"
628
                                  " with '%s', expected %s" %
629
                                  (name, fdef.kind, value.__class__.__name__,
630
                                   verify_fn))
631

    
632
    if hints_fn:
633
      hints_fn(op, datakind, name, value)
634

    
635
    for (fn_flags, fn, valprepfn) in op_data:
636
      if fn_flags is None or fn_flags & field_flags:
637
        # Prepare value if necessary (e.g. compile regular expression)
638
        if valprepfn:
639
          value = valprepfn(value)
640

    
641
        return compat.partial(_WrapBinaryOp, fn, retrieval_fn, value)
642

    
643
    raise errors.ProgrammerError("Unable to find operator implementation"
644
                                 " (op '%s', flags %s)" % (op, field_flags))
645

    
646

    
647
def _CompileFilter(fields, hints, qfilter):
648
  """Converts a query filter into a callable function.
649

650
  See L{_FilterCompilerHelper} for details.
651

652
  @rtype: callable
653

654
  """
655
  return _FilterCompilerHelper(fields)(hints, qfilter)
656

    
657

    
658
class Query:
659
  def __init__(self, fieldlist, selected, qfilter=None, namefield=None):
660
    """Initializes this class.
661

662
    The field definition is a dictionary with the field's name as a key and a
663
    tuple containing, in order, the field definition object
664
    (L{objects.QueryFieldDefinition}, the data kind to help calling code
665
    collect data and a retrieval function. The retrieval function is called
666
    with two parameters, in order, the data container and the item in container
667
    (see L{Query.Query}).
668

669
    Users of this class can call L{RequestedData} before preparing the data
670
    container to determine what data is needed.
671

672
    @type fieldlist: dictionary
673
    @param fieldlist: Field definitions
674
    @type selected: list of strings
675
    @param selected: List of selected fields
676

677
    """
678
    assert namefield is None or namefield in fieldlist
679

    
680
    self._fields = _GetQueryFields(fieldlist, selected)
681

    
682
    self._filter_fn = None
683
    self._requested_names = None
684
    self._filter_datakinds = frozenset()
685

    
686
    if qfilter is not None:
687
      # Collect requested names if wanted
688
      if namefield:
689
        hints = _FilterHints(namefield)
690
      else:
691
        hints = None
692

    
693
      # Build filter function
694
      self._filter_fn = _CompileFilter(fieldlist, hints, qfilter)
695
      if hints:
696
        self._requested_names = hints.RequestedNames()
697
        self._filter_datakinds = hints.ReferencedData()
698

    
699
    if namefield is None:
700
      self._name_fn = None
701
    else:
702
      (_, _, _, self._name_fn) = fieldlist[namefield]
703

    
704
  def RequestedNames(self):
705
    """Returns all names referenced in the filter.
706

707
    If there is no filter or operators are preventing determining the exact
708
    names, C{None} is returned.
709

710
    """
711
    return self._requested_names
712

    
713
  def RequestedData(self):
714
    """Gets requested kinds of data.
715

716
    @rtype: frozenset
717

718
    """
719
    return (self._filter_datakinds |
720
            frozenset(datakind for (_, datakind, _, _) in self._fields
721
                      if datakind is not None))
722

    
723
  def GetFields(self):
724
    """Returns the list of fields for this query.
725

726
    Includes unknown fields.
727

728
    @rtype: List of L{objects.QueryFieldDefinition}
729

730
    """
731
    return GetAllFields(self._fields)
732

    
733
  def Query(self, ctx, sort_by_name=True):
734
    """Execute a query.
735

736
    @param ctx: Data container passed to field retrieval functions, must
737
      support iteration using C{__iter__}
738
    @type sort_by_name: boolean
739
    @param sort_by_name: Whether to sort by name or keep the input data's
740
      ordering
741

742
    """
743
    sort = (self._name_fn and sort_by_name)
744

    
745
    result = []
746

    
747
    for idx, item in enumerate(ctx):
748
      if not (self._filter_fn is None or self._filter_fn(ctx, item)):
749
        continue
750

    
751
      row = [_ProcessResult(fn(ctx, item)) for (_, _, _, fn) in self._fields]
752

    
753
      # Verify result
754
      if __debug__:
755
        _VerifyResultRow(self._fields, row)
756

    
757
      if sort:
758
        (status, name) = _ProcessResult(self._name_fn(ctx, item))
759
        assert status == constants.RS_NORMAL
760
        # TODO: Are there cases where we wouldn't want to use NiceSort?
761
        # Answer: if the name field is non-string...
762
        result.append((utils.NiceSortKey(name), idx, row))
763
      else:
764
        result.append(row)
765

    
766
    if not sort:
767
      return result
768

    
769
    # TODO: Would "heapq" be more efficient than sorting?
770

    
771
    # Sorting in-place instead of using "sorted()"
772
    result.sort()
773

    
774
    assert not result or (len(result[0]) == 3 and len(result[-1]) == 3)
775

    
776
    return map(operator.itemgetter(2), result)
777

    
778
  def OldStyleQuery(self, ctx, sort_by_name=True):
779
    """Query with "old" query result format.
780

781
    See L{Query.Query} for arguments.
782

783
    """
784
    unknown = set(fdef.name for (fdef, _, _, _) in self._fields
785
                  if fdef.kind == QFT_UNKNOWN)
786
    if unknown:
787
      raise errors.OpPrereqError("Unknown output fields selected: %s" %
788
                                 (utils.CommaJoin(unknown), ),
789
                                 errors.ECODE_INVAL)
790

    
791
    return [[value for (_, value) in row]
792
            for row in self.Query(ctx, sort_by_name=sort_by_name)]
793

    
794

    
795
def _ProcessResult(value):
796
  """Converts result values into externally-visible ones.
797

798
  """
799
  if value is _FS_UNKNOWN:
800
    return (RS_UNKNOWN, None)
801
  elif value is _FS_NODATA:
802
    return (RS_NODATA, None)
803
  elif value is _FS_UNAVAIL:
804
    return (RS_UNAVAIL, None)
805
  elif value is _FS_OFFLINE:
806
    return (RS_OFFLINE, None)
807
  else:
808
    return (RS_NORMAL, value)
809

    
810

    
811
def _VerifyResultRow(fields, row):
812
  """Verifies the contents of a query result row.
813

814
  @type fields: list
815
  @param fields: Field definitions for result
816
  @type row: list of tuples
817
  @param row: Row data
818

819
  """
820
  assert len(row) == len(fields)
821
  errs = []
822
  for ((status, value), (fdef, _, _, _)) in zip(row, fields):
823
    if status == RS_NORMAL:
824
      if not _VERIFY_FN[fdef.kind](value):
825
        errs.append("normal field %s fails validation (value is %s)" %
826
                    (fdef.name, value))
827
    elif value is not None:
828
      errs.append("abnormal field %s has a non-None value" % fdef.name)
829
  assert not errs, ("Failed validation: %s in row %s" %
830
                    (utils.CommaJoin(errs), row))
831

    
832

    
833
def _FieldDictKey((fdef, _, flags, fn)):
834
  """Generates key for field dictionary.
835

836
  """
837
  assert fdef.name and fdef.title, "Name and title are required"
838
  assert FIELD_NAME_RE.match(fdef.name)
839
  assert TITLE_RE.match(fdef.title)
840
  assert (DOC_RE.match(fdef.doc) and len(fdef.doc.splitlines()) == 1 and
841
          fdef.doc.strip() == fdef.doc), \
842
         "Invalid description for field '%s'" % fdef.name
843
  assert callable(fn)
844
  assert (flags & ~QFF_ALL) == 0, "Unknown flags for field '%s'" % fdef.name
845

    
846
  return fdef.name
847

    
848

    
849
def _PrepareFieldList(fields, aliases):
850
  """Prepares field list for use by L{Query}.
851

852
  Converts the list to a dictionary and does some verification.
853

854
  @type fields: list of tuples; (L{objects.QueryFieldDefinition}, data
855
      kind, retrieval function)
856
  @param fields: List of fields, see L{Query.__init__} for a better
857
      description
858
  @type aliases: list of tuples; (alias, target)
859
  @param aliases: list of tuples containing aliases; for each
860
      alias/target pair, a duplicate will be created in the field list
861
  @rtype: dict
862
  @return: Field dictionary for L{Query}
863

864
  """
865
  if __debug__:
866
    duplicates = utils.FindDuplicates(fdef.title.lower()
867
                                      for (fdef, _, _, _) in fields)
868
    assert not duplicates, "Duplicate title(s) found: %r" % duplicates
869

    
870
  result = utils.SequenceToDict(fields, key=_FieldDictKey)
871

    
872
  for alias, target in aliases:
873
    assert alias not in result, "Alias %s overrides an existing field" % alias
874
    assert target in result, "Missing target %s for alias %s" % (target, alias)
875
    (fdef, k, flags, fn) = result[target]
876
    fdef = fdef.Copy()
877
    fdef.name = alias
878
    result[alias] = (fdef, k, flags, fn)
879

    
880
  assert len(result) == len(fields) + len(aliases)
881
  assert compat.all(name == fdef.name
882
                    for (name, (fdef, _, _, _)) in result.items())
883

    
884
  return result
885

    
886

    
887
def GetQueryResponse(query, ctx, sort_by_name=True):
888
  """Prepares the response for a query.
889

890
  @type query: L{Query}
891
  @param ctx: Data container, see L{Query.Query}
892
  @type sort_by_name: boolean
893
  @param sort_by_name: Whether to sort by name or keep the input data's
894
    ordering
895

896
  """
897
  return objects.QueryResponse(data=query.Query(ctx, sort_by_name=sort_by_name),
898
                               fields=query.GetFields()).ToDict()
899

    
900

    
901
def QueryFields(fielddefs, selected):
902
  """Returns list of available fields.
903

904
  @type fielddefs: dict
905
  @param fielddefs: Field definitions
906
  @type selected: list of strings
907
  @param selected: List of selected fields
908
  @return: List of L{objects.QueryFieldDefinition}
909

910
  """
911
  if selected is None:
912
    # Client requests all fields, sort by name
913
    fdefs = utils.NiceSort(GetAllFields(fielddefs.values()),
914
                           key=operator.attrgetter("name"))
915
  else:
916
    # Keep order as requested by client
917
    fdefs = Query(fielddefs, selected).GetFields()
918

    
919
  return objects.QueryFieldsResponse(fields=fdefs).ToDict()
920

    
921

    
922
def _MakeField(name, title, kind, doc):
923
  """Wrapper for creating L{objects.QueryFieldDefinition} instances.
924

925
  @param name: Field name as a regular expression
926
  @param title: Human-readable title
927
  @param kind: Field type
928
  @param doc: Human-readable description
929

930
  """
931
  return objects.QueryFieldDefinition(name=name, title=title, kind=kind,
932
                                      doc=doc)
933

    
934

    
935
def _StaticValueInner(value, ctx, _): # pylint: disable=W0613
936
  """Returns a static value.
937

938
  """
939
  return value
940

    
941

    
942
def _StaticValue(value):
943
  """Prepares a function to return a static value.
944

945
  """
946
  return compat.partial(_StaticValueInner, value)
947

    
948

    
949
def _GetNodeRole(node, master_name):
950
  """Determine node role.
951

952
  @type node: L{objects.Node}
953
  @param node: Node object
954
  @type master_name: string
955
  @param master_name: Master node name
956

957
  """
958
  if node.name == master_name:
959
    return constants.NR_MASTER
960
  elif node.master_candidate:
961
    return constants.NR_MCANDIDATE
962
  elif node.drained:
963
    return constants.NR_DRAINED
964
  elif node.offline:
965
    return constants.NR_OFFLINE
966
  else:
967
    return constants.NR_REGULAR
968

    
969

    
970
def _GetItemAttr(attr):
971
  """Returns a field function to return an attribute of the item.
972

973
  @param attr: Attribute name
974

975
  """
976
  getter = operator.attrgetter(attr)
977
  return lambda _, item: getter(item)
978

    
979

    
980
def _GetNDParam(name):
981
  """Return a field function to return an ND parameter out of the context.
982

983
  """
984
  def _helper(ctx, _):
985
    if ctx.ndparams is None:
986
      return _FS_UNAVAIL
987
    else:
988
      return ctx.ndparams.get(name, None)
989
  return _helper
990

    
991

    
992
def _BuildNDFields(is_group):
993
  """Builds all the ndparam fields.
994

995
  @param is_group: whether this is called at group or node level
996

997
  """
998
  if is_group:
999
    field_kind = GQ_CONFIG
1000
  else:
1001
    field_kind = NQ_GROUP
1002
  return [(_MakeField("ndp/%s" % name,
1003
                      constants.NDS_PARAMETER_TITLES.get(name,
1004
                                                         "ndp/%s" % name),
1005
                      _VTToQFT[kind], "The \"%s\" node parameter" % name),
1006
           field_kind, 0, _GetNDParam(name))
1007
          for name, kind in constants.NDS_PARAMETER_TYPES.items()]
1008

    
1009

    
1010
def _ConvWrapInner(convert, fn, ctx, item):
1011
  """Wrapper for converting values.
1012

1013
  @param convert: Conversion function receiving value as single parameter
1014
  @param fn: Retrieval function
1015

1016
  """
1017
  value = fn(ctx, item)
1018

    
1019
  # Is the value an abnormal status?
1020
  if compat.any(value is fs for fs in _FS_ALL):
1021
    # Return right away
1022
    return value
1023

    
1024
  # TODO: Should conversion function also receive context, item or both?
1025
  return convert(value)
1026

    
1027

    
1028
def _ConvWrap(convert, fn):
1029
  """Convenience wrapper for L{_ConvWrapInner}.
1030

1031
  @param convert: Conversion function receiving value as single parameter
1032
  @param fn: Retrieval function
1033

1034
  """
1035
  return compat.partial(_ConvWrapInner, convert, fn)
1036

    
1037

    
1038
def _GetItemTimestamp(getter):
1039
  """Returns function for getting timestamp of item.
1040

1041
  @type getter: callable
1042
  @param getter: Function to retrieve timestamp attribute
1043

1044
  """
1045
  def fn(_, item):
1046
    """Returns a timestamp of item.
1047

1048
    """
1049
    timestamp = getter(item)
1050
    if timestamp is None:
1051
      # Old configs might not have all timestamps
1052
      return _FS_UNAVAIL
1053
    else:
1054
      return timestamp
1055

    
1056
  return fn
1057

    
1058

    
1059
def _GetItemTimestampFields(datatype):
1060
  """Returns common timestamp fields.
1061

1062
  @param datatype: Field data type for use by L{Query.RequestedData}
1063

1064
  """
1065
  return [
1066
    (_MakeField("ctime", "CTime", QFT_TIMESTAMP, "Creation timestamp"),
1067
     datatype, 0, _GetItemTimestamp(operator.attrgetter("ctime"))),
1068
    (_MakeField("mtime", "MTime", QFT_TIMESTAMP, "Modification timestamp"),
1069
     datatype, 0, _GetItemTimestamp(operator.attrgetter("mtime"))),
1070
    ]
1071

    
1072

    
1073
class NodeQueryData:
1074
  """Data container for node data queries.
1075

1076
  """
1077
  def __init__(self, nodes, live_data, master_name, node_to_primary,
1078
               node_to_secondary, groups, oob_support, cluster):
1079
    """Initializes this class.
1080

1081
    """
1082
    self.nodes = nodes
1083
    self.live_data = live_data
1084
    self.master_name = master_name
1085
    self.node_to_primary = node_to_primary
1086
    self.node_to_secondary = node_to_secondary
1087
    self.groups = groups
1088
    self.oob_support = oob_support
1089
    self.cluster = cluster
1090

    
1091
    # Used for individual rows
1092
    self.curlive_data = None
1093
    self.ndparams = None
1094

    
1095
  def __iter__(self):
1096
    """Iterate over all nodes.
1097

1098
    This function has side-effects and only one instance of the resulting
1099
    generator should be used at a time.
1100

1101
    """
1102
    for node in self.nodes:
1103
      group = self.groups.get(node.group, None)
1104
      if group is None:
1105
        self.ndparams = None
1106
      else:
1107
        self.ndparams = self.cluster.FillND(node, group)
1108
      if self.live_data:
1109
        self.curlive_data = self.live_data.get(node.name, None)
1110
      else:
1111
        self.curlive_data = None
1112
      yield node
1113

    
1114

    
1115
#: Fields that are direct attributes of an L{objects.Node} object
1116
_NODE_SIMPLE_FIELDS = {
1117
  "drained": ("Drained", QFT_BOOL, 0, "Whether node is drained"),
1118
  "master_candidate": ("MasterC", QFT_BOOL, 0,
1119
                       "Whether node is a master candidate"),
1120
  "master_capable": ("MasterCapable", QFT_BOOL, 0,
1121
                     "Whether node can become a master candidate"),
1122
  "name": ("Node", QFT_TEXT, QFF_HOSTNAME, "Node name"),
1123
  "offline": ("Offline", QFT_BOOL, 0, "Whether node is marked offline"),
1124
  "serial_no": ("SerialNo", QFT_NUMBER, 0, _SERIAL_NO_DOC % "Node"),
1125
  "uuid": ("UUID", QFT_TEXT, 0, "Node UUID"),
1126
  "vm_capable": ("VMCapable", QFT_BOOL, 0, "Whether node can host instances"),
1127
  }
1128

    
1129

    
1130
#: Fields requiring talking to the node
1131
# Note that none of these are available for non-vm_capable nodes
1132
_NODE_LIVE_FIELDS = {
1133
  "bootid": ("BootID", QFT_TEXT, "bootid",
1134
             "Random UUID renewed for each system reboot, can be used"
1135
             " for detecting reboots by tracking changes"),
1136
  "cnodes": ("CNodes", QFT_NUMBER, "cpu_nodes",
1137
             "Number of NUMA domains on node (if exported by hypervisor)"),
1138
  "csockets": ("CSockets", QFT_NUMBER, "cpu_sockets",
1139
               "Number of physical CPU sockets (if exported by hypervisor)"),
1140
  "ctotal": ("CTotal", QFT_NUMBER, "cpu_total", "Number of logical processors"),
1141
  "dfree": ("DFree", QFT_UNIT, "vg_free",
1142
            "Available disk space in volume group"),
1143
  "dtotal": ("DTotal", QFT_UNIT, "vg_size",
1144
             "Total disk space in volume group used for instance disk"
1145
             " allocation"),
1146
  "mfree": ("MFree", QFT_UNIT, "memory_free",
1147
            "Memory available for instance allocations"),
1148
  "mnode": ("MNode", QFT_UNIT, "memory_dom0",
1149
            "Amount of memory used by node (dom0 for Xen)"),
1150
  "mtotal": ("MTotal", QFT_UNIT, "memory_total",
1151
             "Total amount of memory of physical machine"),
1152
  }
1153

    
1154

    
1155
def _GetGroup(cb):
1156
  """Build function for calling another function with an node group.
1157

1158
  @param cb: The callback to be called with the nodegroup
1159

1160
  """
1161
  def fn(ctx, node):
1162
    """Get group data for a node.
1163

1164
    @type ctx: L{NodeQueryData}
1165
    @type inst: L{objects.Node}
1166
    @param inst: Node object
1167

1168
    """
1169
    ng = ctx.groups.get(node.group, None)
1170
    if ng is None:
1171
      # Nodes always have a group, or the configuration is corrupt
1172
      return _FS_UNAVAIL
1173

    
1174
    return cb(ctx, node, ng)
1175

    
1176
  return fn
1177

    
1178

    
1179
def _GetNodeGroup(ctx, node, ng): # pylint: disable=W0613
1180
  """Returns the name of a node's group.
1181

1182
  @type ctx: L{NodeQueryData}
1183
  @type node: L{objects.Node}
1184
  @param node: Node object
1185
  @type ng: L{objects.NodeGroup}
1186
  @param ng: The node group this node belongs to
1187

1188
  """
1189
  return ng.name
1190

    
1191

    
1192
def _GetNodePower(ctx, node):
1193
  """Returns the node powered state
1194

1195
  @type ctx: L{NodeQueryData}
1196
  @type node: L{objects.Node}
1197
  @param node: Node object
1198

1199
  """
1200
  if ctx.oob_support[node.name]:
1201
    return node.powered
1202

    
1203
  return _FS_UNAVAIL
1204

    
1205

    
1206
def _GetNdParams(ctx, node, ng):
1207
  """Returns the ndparams for this node.
1208

1209
  @type ctx: L{NodeQueryData}
1210
  @type node: L{objects.Node}
1211
  @param node: Node object
1212
  @type ng: L{objects.NodeGroup}
1213
  @param ng: The node group this node belongs to
1214

1215
  """
1216
  return ctx.cluster.SimpleFillND(ng.FillND(node))
1217

    
1218

    
1219
def _GetLiveNodeField(field, kind, ctx, node):
1220
  """Gets the value of a "live" field from L{NodeQueryData}.
1221

1222
  @param field: Live field name
1223
  @param kind: Data kind, one of L{constants.QFT_ALL}
1224
  @type ctx: L{NodeQueryData}
1225
  @type node: L{objects.Node}
1226
  @param node: Node object
1227

1228
  """
1229
  if node.offline:
1230
    return _FS_OFFLINE
1231

    
1232
  if not node.vm_capable:
1233
    return _FS_UNAVAIL
1234

    
1235
  if not ctx.curlive_data:
1236
    return _FS_NODATA
1237

    
1238
  return _GetStatsField(field, kind, ctx.curlive_data)
1239

    
1240

    
1241
def _GetStatsField(field, kind, data):
1242
  """Gets a value from live statistics.
1243

1244
  If the value is not found, L{_FS_UNAVAIL} is returned. If the field kind is
1245
  numeric a conversion to integer is attempted. If that fails, L{_FS_UNAVAIL}
1246
  is returned.
1247

1248
  @param field: Live field name
1249
  @param kind: Data kind, one of L{constants.QFT_ALL}
1250
  @type data: dict
1251
  @param data: Statistics
1252

1253
  """
1254
  try:
1255
    value = data[field]
1256
  except KeyError:
1257
    return _FS_UNAVAIL
1258

    
1259
  if kind == QFT_TEXT:
1260
    return value
1261

    
1262
  assert kind in (QFT_NUMBER, QFT_UNIT)
1263

    
1264
  # Try to convert into number
1265
  try:
1266
    return int(value)
1267
  except (ValueError, TypeError):
1268
    logging.exception("Failed to convert node field '%s' (value %r) to int",
1269
                      field, value)
1270
    return _FS_UNAVAIL
1271

    
1272

    
1273
def _GetNodeHvState(_, node):
1274
  """Converts node's hypervisor state for query result.
1275

1276
  """
1277
  hv_state = node.hv_state
1278

    
1279
  if hv_state is None:
1280
    return _FS_UNAVAIL
1281

    
1282
  return dict((name, value.ToDict()) for (name, value) in hv_state.items())
1283

    
1284

    
1285
def _GetNodeDiskState(_, node):
1286
  """Converts node's disk state for query result.
1287

1288
  """
1289
  disk_state = node.disk_state
1290

    
1291
  if disk_state is None:
1292
    return _FS_UNAVAIL
1293

    
1294
  return dict((disk_kind, dict((name, value.ToDict())
1295
                               for (name, value) in kind_state.items()))
1296
              for (disk_kind, kind_state) in disk_state.items())
1297

    
1298

    
1299
def _BuildNodeFields():
1300
  """Builds list of fields for node queries.
1301

1302
  """
1303
  fields = [
1304
    (_MakeField("pip", "PrimaryIP", QFT_TEXT, "Primary IP address"),
1305
     NQ_CONFIG, 0, _GetItemAttr("primary_ip")),
1306
    (_MakeField("sip", "SecondaryIP", QFT_TEXT, "Secondary IP address"),
1307
     NQ_CONFIG, 0, _GetItemAttr("secondary_ip")),
1308
    (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), NQ_CONFIG, 0,
1309
     lambda ctx, node: list(node.GetTags())),
1310
    (_MakeField("master", "IsMaster", QFT_BOOL, "Whether node is master"),
1311
     NQ_CONFIG, 0, lambda ctx, node: node.name == ctx.master_name),
1312
    (_MakeField("group", "Group", QFT_TEXT, "Node group"), NQ_GROUP, 0,
1313
     _GetGroup(_GetNodeGroup)),
1314
    (_MakeField("group.uuid", "GroupUUID", QFT_TEXT, "UUID of node group"),
1315
     NQ_CONFIG, 0, _GetItemAttr("group")),
1316
    (_MakeField("powered", "Powered", QFT_BOOL,
1317
                "Whether node is thought to be powered on"),
1318
     NQ_OOB, 0, _GetNodePower),
1319
    (_MakeField("ndparams", "NodeParameters", QFT_OTHER,
1320
                "Merged node parameters"),
1321
     NQ_GROUP, 0, _GetGroup(_GetNdParams)),
1322
    (_MakeField("custom_ndparams", "CustomNodeParameters", QFT_OTHER,
1323
                "Custom node parameters"),
1324
      NQ_GROUP, 0, _GetItemAttr("ndparams")),
1325
    (_MakeField("hv_state", "HypervisorState", QFT_OTHER, "Hypervisor state"),
1326
     NQ_CONFIG, 0, _GetNodeHvState),
1327
    (_MakeField("disk_state", "DiskState", QFT_OTHER, "Disk state"),
1328
     NQ_CONFIG, 0, _GetNodeDiskState),
1329
    ]
1330

    
1331
  fields.extend(_BuildNDFields(False))
1332

    
1333
  # Node role
1334
  role_values = (constants.NR_MASTER, constants.NR_MCANDIDATE,
1335
                 constants.NR_REGULAR, constants.NR_DRAINED,
1336
                 constants.NR_OFFLINE)
1337
  role_doc = ("Node role; \"%s\" for master, \"%s\" for master candidate,"
1338
              " \"%s\" for regular, \"%s\" for drained, \"%s\" for offline" %
1339
              role_values)
1340
  fields.append((_MakeField("role", "Role", QFT_TEXT, role_doc), NQ_CONFIG, 0,
1341
                 lambda ctx, node: _GetNodeRole(node, ctx.master_name)))
1342
  assert set(role_values) == constants.NR_ALL
1343

    
1344
  def _GetLength(getter):
1345
    return lambda ctx, node: len(getter(ctx)[node.name])
1346

    
1347
  def _GetList(getter):
1348
    return lambda ctx, node: list(getter(ctx)[node.name])
1349

    
1350
  # Add fields operating on instance lists
1351
  for prefix, titleprefix, docword, getter in \
1352
      [("p", "Pri", "primary", operator.attrgetter("node_to_primary")),
1353
       ("s", "Sec", "secondary", operator.attrgetter("node_to_secondary"))]:
1354
    # TODO: Allow filterting by hostname in list
1355
    fields.extend([
1356
      (_MakeField("%sinst_cnt" % prefix, "%sinst" % prefix.upper(), QFT_NUMBER,
1357
                  "Number of instances with this node as %s" % docword),
1358
       NQ_INST, 0, _GetLength(getter)),
1359
      (_MakeField("%sinst_list" % prefix, "%sInstances" % titleprefix,
1360
                  QFT_OTHER,
1361
                  "List of instances with this node as %s" % docword),
1362
       NQ_INST, 0, _GetList(getter)),
1363
      ])
1364

    
1365
  # Add simple fields
1366
  fields.extend([
1367
    (_MakeField(name, title, kind, doc), NQ_CONFIG, flags, _GetItemAttr(name))
1368
    for (name, (title, kind, flags, doc)) in _NODE_SIMPLE_FIELDS.items()])
1369

    
1370
  # Add fields requiring live data
1371
  fields.extend([
1372
    (_MakeField(name, title, kind, doc), NQ_LIVE, 0,
1373
     compat.partial(_GetLiveNodeField, nfield, kind))
1374
    for (name, (title, kind, nfield, doc)) in _NODE_LIVE_FIELDS.items()])
1375

    
1376
  # Add timestamps
1377
  fields.extend(_GetItemTimestampFields(NQ_CONFIG))
1378

    
1379
  return _PrepareFieldList(fields, [])
1380

    
1381

    
1382
class InstanceQueryData:
1383
  """Data container for instance data queries.
1384

1385
  """
1386
  def __init__(self, instances, cluster, disk_usage, offline_nodes, bad_nodes,
1387
               live_data, wrongnode_inst, console, nodes, groups, networks):
1388
    """Initializes this class.
1389

1390
    @param instances: List of instance objects
1391
    @param cluster: Cluster object
1392
    @type disk_usage: dict; instance name as key
1393
    @param disk_usage: Per-instance disk usage
1394
    @type offline_nodes: list of strings
1395
    @param offline_nodes: List of offline nodes
1396
    @type bad_nodes: list of strings
1397
    @param bad_nodes: List of faulty nodes
1398
    @type live_data: dict; instance name as key
1399
    @param live_data: Per-instance live data
1400
    @type wrongnode_inst: set
1401
    @param wrongnode_inst: Set of instances running on wrong node(s)
1402
    @type console: dict; instance name as key
1403
    @param console: Per-instance console information
1404
    @type nodes: dict; node name as key
1405
    @param nodes: Node objects
1406
    @type networks: dict; net_uuid as key
1407
    @param networks: Network objects
1408

1409
    """
1410
    assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
1411
           "Offline nodes not included in bad nodes"
1412
    assert not (set(live_data.keys()) & set(bad_nodes)), \
1413
           "Found live data for bad or offline nodes"
1414

    
1415
    self.instances = instances
1416
    self.cluster = cluster
1417
    self.disk_usage = disk_usage
1418
    self.offline_nodes = offline_nodes
1419
    self.bad_nodes = bad_nodes
1420
    self.live_data = live_data
1421
    self.wrongnode_inst = wrongnode_inst
1422
    self.console = console
1423
    self.nodes = nodes
1424
    self.groups = groups
1425
    self.networks = networks
1426

    
1427
    # Used for individual rows
1428
    self.inst_hvparams = None
1429
    self.inst_beparams = None
1430
    self.inst_osparams = None
1431
    self.inst_nicparams = None
1432

    
1433
  def __iter__(self):
1434
    """Iterate over all instances.
1435

1436
    This function has side-effects and only one instance of the resulting
1437
    generator should be used at a time.
1438

1439
    """
1440
    for inst in self.instances:
1441
      self.inst_hvparams = self.cluster.FillHV(inst, skip_globals=True)
1442
      self.inst_beparams = self.cluster.FillBE(inst)
1443
      self.inst_osparams = self.cluster.SimpleFillOS(inst.os, inst.osparams)
1444
      self.inst_nicparams = [self.cluster.SimpleFillNIC(nic.nicparams)
1445
                             for nic in inst.nics]
1446

    
1447
      yield inst
1448

    
1449

    
1450
def _GetInstOperState(ctx, inst):
1451
  """Get instance's operational status.
1452

1453
  @type ctx: L{InstanceQueryData}
1454
  @type inst: L{objects.Instance}
1455
  @param inst: Instance object
1456

1457
  """
1458
  # Can't use RS_OFFLINE here as it would describe the instance to
1459
  # be offline when we actually don't know due to missing data
1460
  if inst.primary_node in ctx.bad_nodes:
1461
    return _FS_NODATA
1462
  else:
1463
    return bool(ctx.live_data.get(inst.name))
1464

    
1465

    
1466
def _GetInstLiveData(name):
1467
  """Build function for retrieving live data.
1468

1469
  @type name: string
1470
  @param name: Live data field name
1471

1472
  """
1473
  def fn(ctx, inst):
1474
    """Get live data for an instance.
1475

1476
    @type ctx: L{InstanceQueryData}
1477
    @type inst: L{objects.Instance}
1478
    @param inst: Instance object
1479

1480
    """
1481
    if (inst.primary_node in ctx.bad_nodes or
1482
        inst.primary_node in ctx.offline_nodes):
1483
      # Can't use RS_OFFLINE here as it would describe the instance to be
1484
      # offline when we actually don't know due to missing data
1485
      return _FS_NODATA
1486

    
1487
    if inst.name in ctx.live_data:
1488
      data = ctx.live_data[inst.name]
1489
      if name in data:
1490
        return data[name]
1491

    
1492
    return _FS_UNAVAIL
1493

    
1494
  return fn
1495

    
1496

    
1497
def _GetInstStatus(ctx, inst):
1498
  """Get instance status.
1499

1500
  @type ctx: L{InstanceQueryData}
1501
  @type inst: L{objects.Instance}
1502
  @param inst: Instance object
1503

1504
  """
1505
  if inst.primary_node in ctx.offline_nodes:
1506
    return constants.INSTST_NODEOFFLINE
1507

    
1508
  if inst.primary_node in ctx.bad_nodes:
1509
    return constants.INSTST_NODEDOWN
1510

    
1511
  if bool(ctx.live_data.get(inst.name)):
1512
    if inst.name in ctx.wrongnode_inst:
1513
      return constants.INSTST_WRONGNODE
1514
    elif inst.admin_state == constants.ADMINST_UP:
1515
      return constants.INSTST_RUNNING
1516
    else:
1517
      return constants.INSTST_ERRORUP
1518

    
1519
  if inst.admin_state == constants.ADMINST_UP:
1520
    return constants.INSTST_ERRORDOWN
1521
  elif inst.admin_state == constants.ADMINST_DOWN:
1522
    return constants.INSTST_ADMINDOWN
1523

    
1524
  return constants.INSTST_ADMINOFFLINE
1525

    
1526

    
1527
def _GetInstDisk(index, cb):
1528
  """Build function for calling another function with an instance Disk.
1529

1530
  @type index: int
1531
  @param index: Disk index
1532
  @type cb: callable
1533
  @param cb: Callback
1534

1535
  """
1536
  def fn(ctx, inst):
1537
    """Call helper function with instance Disk.
1538

1539
    @type ctx: L{InstanceQueryData}
1540
    @type inst: L{objects.Instance}
1541
    @param inst: Instance object
1542

1543
    """
1544
    try:
1545
      nic = inst.disks[index]
1546
    except IndexError:
1547
      return _FS_UNAVAIL
1548

    
1549
    return cb(ctx, index, nic)
1550

    
1551
  return fn
1552

    
1553

    
1554
def _GetInstDiskSize(ctx, _, disk): # pylint: disable=W0613
1555
  """Get a Disk's size.
1556

1557
  @type ctx: L{InstanceQueryData}
1558
  @type disk: L{objects.Disk}
1559
  @param disk: The Disk object
1560

1561
  """
1562
  if disk.size is None:
1563
    return _FS_UNAVAIL
1564
  else:
1565
    return disk.size
1566

    
1567

    
1568
def _GetInstDeviceName(ctx, _, device): # pylint: disable=W0613
1569
  """Get a Device's Name.
1570

1571
  @type ctx: L{InstanceQueryData}
1572
  @type device: L{objects.NIC} or L{objects.Disk}
1573
  @param device: The NIC or Disk object
1574

1575
  """
1576
  if device.name is None:
1577
    return _FS_UNAVAIL
1578
  else:
1579
    return device.name
1580

    
1581

    
1582
def _GetInstDeviceUUID(ctx, _, device): # pylint: disable=W0613
1583
  """Get a Device's UUID.
1584

1585
  @type ctx: L{InstanceQueryData}
1586
  @type device: L{objects.NIC} or L{objects.Disk}
1587
  @param device: The NIC or Disk object
1588

1589
  """
1590
  if device.uuid is None:
1591
    return _FS_UNAVAIL
1592
  else:
1593
    return device.uuid
1594

    
1595

    
1596
def _GetInstNic(index, cb):
1597
  """Build function for calling another function with an instance NIC.
1598

1599
  @type index: int
1600
  @param index: NIC index
1601
  @type cb: callable
1602
  @param cb: Callback
1603

1604
  """
1605
  def fn(ctx, inst):
1606
    """Call helper function with instance NIC.
1607

1608
    @type ctx: L{InstanceQueryData}
1609
    @type inst: L{objects.Instance}
1610
    @param inst: Instance object
1611

1612
    """
1613
    try:
1614
      nic = inst.nics[index]
1615
    except IndexError:
1616
      return _FS_UNAVAIL
1617

    
1618
    return cb(ctx, index, nic)
1619

    
1620
  return fn
1621

    
1622

    
1623
def _GetInstNicNetworkName(ctx, _, nic): # pylint: disable=W0613
1624
  """Get a NIC's Network.
1625

1626
  @type ctx: L{InstanceQueryData}
1627
  @type nic: L{objects.NIC}
1628
  @param nic: NIC object
1629

1630
  """
1631
  if nic.network is None:
1632
    return _FS_UNAVAIL
1633
  else:
1634
    return ctx.networks[nic.network].name
1635

    
1636

    
1637
def _GetInstNicNetwork(ctx, _, nic): # pylint: disable=W0613
1638
  """Get a NIC's Network.
1639

1640
  @type ctx: L{InstanceQueryData}
1641
  @type nic: L{objects.NIC}
1642
  @param nic: NIC object
1643

1644
  """
1645
  if nic.network is None:
1646
    return _FS_UNAVAIL
1647
  else:
1648
    return nic.network
1649

    
1650

    
1651
def _GetInstNicIp(ctx, _, nic): # pylint: disable=W0613
1652
  """Get a NIC's IP address.
1653

1654
  @type ctx: L{InstanceQueryData}
1655
  @type nic: L{objects.NIC}
1656
  @param nic: NIC object
1657

1658
  """
1659
  if nic.ip is None:
1660
    return _FS_UNAVAIL
1661
  else:
1662
    return nic.ip
1663

    
1664

    
1665
def _GetInstNicBridge(ctx, index, _):
1666
  """Get a NIC's bridge.
1667

1668
  @type ctx: L{InstanceQueryData}
1669
  @type index: int
1670
  @param index: NIC index
1671

1672
  """
1673
  assert len(ctx.inst_nicparams) >= index
1674

    
1675
  nicparams = ctx.inst_nicparams[index]
1676

    
1677
  if nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1678
    return nicparams[constants.NIC_LINK]
1679
  else:
1680
    return _FS_UNAVAIL
1681

    
1682

    
1683
def _GetInstAllNicNetworkNames(ctx, inst):
1684
  """Get all network names for an instance.
1685

1686
  @type ctx: L{InstanceQueryData}
1687
  @type inst: L{objects.Instance}
1688
  @param inst: Instance object
1689

1690
  """
1691
  result = []
1692

    
1693
  for nic in inst.nics:
1694
    name = None
1695
    if nic.network:
1696
      name = ctx.networks[nic.network].name
1697
    result.append(name)
1698

    
1699
  assert len(result) == len(inst.nics)
1700

    
1701
  return result
1702

    
1703

    
1704
def _GetInstAllNicBridges(ctx, inst):
1705
  """Get all network bridges for an instance.
1706

1707
  @type ctx: L{InstanceQueryData}
1708
  @type inst: L{objects.Instance}
1709
  @param inst: Instance object
1710

1711
  """
1712
  assert len(ctx.inst_nicparams) == len(inst.nics)
1713

    
1714
  result = []
1715

    
1716
  for nicp in ctx.inst_nicparams:
1717
    if nicp[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1718
      result.append(nicp[constants.NIC_LINK])
1719
    else:
1720
      result.append(None)
1721

    
1722
  assert len(result) == len(inst.nics)
1723

    
1724
  return result
1725

    
1726

    
1727
def _GetInstNicParam(name):
1728
  """Build function for retrieving a NIC parameter.
1729

1730
  @type name: string
1731
  @param name: Parameter name
1732

1733
  """
1734
  def fn(ctx, index, _):
1735
    """Get a NIC's bridge.
1736

1737
    @type ctx: L{InstanceQueryData}
1738
    @type inst: L{objects.Instance}
1739
    @param inst: Instance object
1740
    @type nic: L{objects.NIC}
1741
    @param nic: NIC object
1742

1743
    """
1744
    assert len(ctx.inst_nicparams) >= index
1745
    return ctx.inst_nicparams[index][name]
1746

    
1747
  return fn
1748

    
1749

    
1750
def _GetInstanceNetworkFields():
1751
  """Get instance fields involving network interfaces.
1752

1753
  @return: Tuple containing list of field definitions used as input for
1754
    L{_PrepareFieldList} and a list of aliases
1755

1756
  """
1757
  nic_mac_fn = lambda ctx, _, nic: nic.mac
1758
  nic_mode_fn = _GetInstNicParam(constants.NIC_MODE)
1759
  nic_link_fn = _GetInstNicParam(constants.NIC_LINK)
1760

    
1761
  fields = [
1762
    # All NICs
1763
    (_MakeField("nic.count", "NICs", QFT_NUMBER,
1764
                "Number of network interfaces"),
1765
     IQ_CONFIG, 0, lambda ctx, inst: len(inst.nics)),
1766
    (_MakeField("nic.macs", "NIC_MACs", QFT_OTHER,
1767
                "List containing each network interface's MAC address"),
1768
     IQ_CONFIG, 0, lambda ctx, inst: [nic.mac for nic in inst.nics]),
1769
    (_MakeField("nic.ips", "NIC_IPs", QFT_OTHER,
1770
                "List containing each network interface's IP address"),
1771
     IQ_CONFIG, 0, lambda ctx, inst: [nic.ip for nic in inst.nics]),
1772
    (_MakeField("nic.names", "NIC_Names", QFT_OTHER,
1773
                "List containing each network interface's name"),
1774
     IQ_CONFIG, 0, lambda ctx, inst: [nic.name for nic in inst.nics]),
1775
    (_MakeField("nic.uuids", "NIC_UUIDs", QFT_OTHER,
1776
                "List containing each network interface's UUID"),
1777
     IQ_CONFIG, 0, lambda ctx, inst: [nic.uuid for nic in inst.nics]),
1778
    (_MakeField("nic.modes", "NIC_modes", QFT_OTHER,
1779
                "List containing each network interface's mode"), IQ_CONFIG, 0,
1780
     lambda ctx, inst: [nicp[constants.NIC_MODE]
1781
                        for nicp in ctx.inst_nicparams]),
1782
    (_MakeField("nic.links", "NIC_links", QFT_OTHER,
1783
                "List containing each network interface's link"), IQ_CONFIG, 0,
1784
     lambda ctx, inst: [nicp[constants.NIC_LINK]
1785
                        for nicp in ctx.inst_nicparams]),
1786
    (_MakeField("nic.bridges", "NIC_bridges", QFT_OTHER,
1787
                "List containing each network interface's bridge"),
1788
     IQ_CONFIG, 0, _GetInstAllNicBridges),
1789
    (_MakeField("nic.networks", "NIC_networks", QFT_OTHER,
1790
                "List containing each interface's network"), IQ_CONFIG, 0,
1791
     lambda ctx, inst: [nic.network for nic in inst.nics]),
1792
    (_MakeField("nic.networks.names", "NIC_networks_names", QFT_OTHER,
1793
                "List containing each interface's network"),
1794
     IQ_NETWORKS, 0, _GetInstAllNicNetworkNames)
1795
    ]
1796

    
1797
  # NICs by number
1798
  for i in range(constants.MAX_NICS):
1799
    numtext = utils.FormatOrdinal(i + 1)
1800
    fields.extend([
1801
      (_MakeField("nic.ip/%s" % i, "NicIP/%s" % i, QFT_TEXT,
1802
                  "IP address of %s network interface" % numtext),
1803
       IQ_CONFIG, 0, _GetInstNic(i, _GetInstNicIp)),
1804
      (_MakeField("nic.mac/%s" % i, "NicMAC/%s" % i, QFT_TEXT,
1805
                  "MAC address of %s network interface" % numtext),
1806
       IQ_CONFIG, 0, _GetInstNic(i, nic_mac_fn)),
1807
      (_MakeField("nic.name/%s" % i, "NicName/%s" % i, QFT_TEXT,
1808
                  "Name address of %s network interface" % numtext),
1809
       IQ_CONFIG, 0, _GetInstNic(i, _GetInstDeviceName)),
1810
      (_MakeField("nic.uuid/%s" % i, "NicUUID/%s" % i, QFT_TEXT,
1811
                  "UUID address of %s network interface" % numtext),
1812
       IQ_CONFIG, 0, _GetInstNic(i, _GetInstDeviceUUID)),
1813
      (_MakeField("nic.mode/%s" % i, "NicMode/%s" % i, QFT_TEXT,
1814
                  "Mode of %s network interface" % numtext),
1815
       IQ_CONFIG, 0, _GetInstNic(i, nic_mode_fn)),
1816
      (_MakeField("nic.link/%s" % i, "NicLink/%s" % i, QFT_TEXT,
1817
                  "Link of %s network interface" % numtext),
1818
       IQ_CONFIG, 0, _GetInstNic(i, nic_link_fn)),
1819
      (_MakeField("nic.bridge/%s" % i, "NicBridge/%s" % i, QFT_TEXT,
1820
                  "Bridge of %s network interface" % numtext),
1821
       IQ_CONFIG, 0, _GetInstNic(i, _GetInstNicBridge)),
1822
      (_MakeField("nic.network/%s" % i, "NicNetwork/%s" % i, QFT_TEXT,
1823
                  "Network of %s network interface" % numtext),
1824
       IQ_CONFIG, 0, _GetInstNic(i, _GetInstNicNetwork)),
1825
      (_MakeField("nic.network.name/%s" % i, "NicNetworkName/%s" % i, QFT_TEXT,
1826
                  "Network name of %s network interface" % numtext),
1827
       IQ_NETWORKS, 0, _GetInstNic(i, _GetInstNicNetworkName)),
1828
      ])
1829

    
1830
  aliases = [
1831
    # Legacy fields for first NIC
1832
    ("ip", "nic.ip/0"),
1833
    ("mac", "nic.mac/0"),
1834
    ("bridge", "nic.bridge/0"),
1835
    ("nic_mode", "nic.mode/0"),
1836
    ("nic_link", "nic.link/0"),
1837
    ("nic_network", "nic.network/0"),
1838
    ]
1839

    
1840
  return (fields, aliases)
1841

    
1842

    
1843
def _GetInstDiskUsage(ctx, inst):
1844
  """Get disk usage for an instance.
1845

1846
  @type ctx: L{InstanceQueryData}
1847
  @type inst: L{objects.Instance}
1848
  @param inst: Instance object
1849

1850
  """
1851
  usage = ctx.disk_usage[inst.name]
1852

    
1853
  if usage is None:
1854
    usage = 0
1855

    
1856
  return usage
1857

    
1858

    
1859
def _GetInstanceConsole(ctx, inst):
1860
  """Get console information for instance.
1861

1862
  @type ctx: L{InstanceQueryData}
1863
  @type inst: L{objects.Instance}
1864
  @param inst: Instance object
1865

1866
  """
1867
  consinfo = ctx.console[inst.name]
1868

    
1869
  if consinfo is None:
1870
    return _FS_UNAVAIL
1871

    
1872
  return consinfo
1873

    
1874

    
1875
def _GetInstanceDiskFields():
1876
  """Get instance fields involving disks.
1877

1878
  @return: List of field definitions used as input for L{_PrepareFieldList}
1879

1880
  """
1881
  fields = [
1882
    (_MakeField("disk_usage", "DiskUsage", QFT_UNIT,
1883
                "Total disk space used by instance on each of its nodes;"
1884
                " this is not the disk size visible to the instance, but"
1885
                " the usage on the node"),
1886
     IQ_DISKUSAGE, 0, _GetInstDiskUsage),
1887
    (_MakeField("disk.count", "Disks", QFT_NUMBER, "Number of disks"),
1888
     IQ_CONFIG, 0, lambda ctx, inst: len(inst.disks)),
1889
    (_MakeField("disk.sizes", "Disk_sizes", QFT_OTHER, "List of disk sizes"),
1890
     IQ_CONFIG, 0, lambda ctx, inst: [disk.size for disk in inst.disks]),
1891
    (_MakeField("disk.names", "Disk_names", QFT_OTHER, "List of disk names"),
1892
     IQ_CONFIG, 0, lambda ctx, inst: [disk.name for disk in inst.disks]),
1893
    (_MakeField("disk.uuids", "Disk_UUIDs", QFT_OTHER, "List of disk UUIDs"),
1894
     IQ_CONFIG, 0, lambda ctx, inst: [disk.uuid for disk in inst.disks]),
1895
    ]
1896

    
1897
  # Disks by number
1898
  for i in range(constants.MAX_DISKS):
1899
    numtext = utils.FormatOrdinal(i + 1)
1900
    fields.extend([
1901
        (_MakeField("disk.size/%s" % i, "Disk/%s" % i, QFT_UNIT,
1902
                    "Disk size of %s disk" % numtext),
1903
        IQ_CONFIG, 0, _GetInstDisk(i, _GetInstDiskSize)),
1904
        (_MakeField("disk.name/%s" % i, "DiskName/%s" % i, QFT_TEXT,
1905
                    "Name of %s disk" % numtext),
1906
        IQ_CONFIG, 0, _GetInstDisk(i, _GetInstDeviceName)),
1907
        (_MakeField("disk.uuid/%s" % i, "DiskUUID/%s" % i, QFT_TEXT,
1908
                    "UUID of %s disk" % numtext),
1909
        IQ_CONFIG, 0, _GetInstDisk(i, _GetInstDeviceUUID))])
1910

    
1911
  return fields
1912

    
1913

    
1914
def _GetInstanceParameterFields():
1915
  """Get instance fields involving parameters.
1916

1917
  @return: List of field definitions used as input for L{_PrepareFieldList}
1918

1919
  """
1920
  fields = [
1921
    # Filled parameters
1922
    (_MakeField("hvparams", "HypervisorParameters", QFT_OTHER,
1923
                "Hypervisor parameters (merged)"),
1924
     IQ_CONFIG, 0, lambda ctx, _: ctx.inst_hvparams),
1925
    (_MakeField("beparams", "BackendParameters", QFT_OTHER,
1926
                "Backend parameters (merged)"),
1927
     IQ_CONFIG, 0, lambda ctx, _: ctx.inst_beparams),
1928
    (_MakeField("osparams", "OpSysParameters", QFT_OTHER,
1929
                "Operating system parameters (merged)"),
1930
     IQ_CONFIG, 0, lambda ctx, _: ctx.inst_osparams),
1931

    
1932
    # Unfilled parameters
1933
    (_MakeField("custom_hvparams", "CustomHypervisorParameters", QFT_OTHER,
1934
                "Custom hypervisor parameters"),
1935
     IQ_CONFIG, 0, _GetItemAttr("hvparams")),
1936
    (_MakeField("custom_beparams", "CustomBackendParameters", QFT_OTHER,
1937
                "Custom backend parameters",),
1938
     IQ_CONFIG, 0, _GetItemAttr("beparams")),
1939
    (_MakeField("custom_osparams", "CustomOpSysParameters", QFT_OTHER,
1940
                "Custom operating system parameters",),
1941
     IQ_CONFIG, 0, _GetItemAttr("osparams")),
1942
    (_MakeField("custom_nicparams", "CustomNicParameters", QFT_OTHER,
1943
                "Custom network interface parameters"),
1944
     IQ_CONFIG, 0, lambda ctx, inst: [nic.nicparams for nic in inst.nics]),
1945
    ]
1946

    
1947
  # HV params
1948
  def _GetInstHvParam(name):
1949
    return lambda ctx, _: ctx.inst_hvparams.get(name, _FS_UNAVAIL)
1950

    
1951
  fields.extend([
1952
    (_MakeField("hv/%s" % name,
1953
                constants.HVS_PARAMETER_TITLES.get(name, "hv/%s" % name),
1954
                _VTToQFT[kind], "The \"%s\" hypervisor parameter" % name),
1955
     IQ_CONFIG, 0, _GetInstHvParam(name))
1956
    for name, kind in constants.HVS_PARAMETER_TYPES.items()
1957
    if name not in constants.HVC_GLOBALS])
1958

    
1959
  # BE params
1960
  def _GetInstBeParam(name):
1961
    return lambda ctx, _: ctx.inst_beparams.get(name, None)
1962

    
1963
  fields.extend([
1964
    (_MakeField("be/%s" % name,
1965
                constants.BES_PARAMETER_TITLES.get(name, "be/%s" % name),
1966
                _VTToQFT[kind], "The \"%s\" backend parameter" % name),
1967
     IQ_CONFIG, 0, _GetInstBeParam(name))
1968
    for name, kind in constants.BES_PARAMETER_TYPES.items()])
1969

    
1970
  return fields
1971

    
1972

    
1973
_INST_SIMPLE_FIELDS = {
1974
  "disk_template": ("Disk_template", QFT_TEXT, 0, "Instance disk template"),
1975
  "hypervisor": ("Hypervisor", QFT_TEXT, 0, "Hypervisor name"),
1976
  "name": ("Instance", QFT_TEXT, QFF_HOSTNAME, "Instance name"),
1977
  # Depending on the hypervisor, the port can be None
1978
  "network_port": ("Network_port", QFT_OTHER, 0,
1979
                   "Instance network port if available (e.g. for VNC console)"),
1980
  "os": ("OS", QFT_TEXT, 0, "Operating system"),
1981
  "serial_no": ("SerialNo", QFT_NUMBER, 0, _SERIAL_NO_DOC % "Instance"),
1982
  "uuid": ("UUID", QFT_TEXT, 0, "Instance UUID"),
1983
  }
1984

    
1985

    
1986
def _GetInstNodeGroup(ctx, default, node_name):
1987
  """Gets group UUID of an instance node.
1988

1989
  @type ctx: L{InstanceQueryData}
1990
  @param default: Default value
1991
  @type node_name: string
1992
  @param node_name: Node name
1993

1994
  """
1995
  try:
1996
    node = ctx.nodes[node_name]
1997
  except KeyError:
1998
    return default
1999
  else:
2000
    return node.group
2001

    
2002

    
2003
def _GetInstNodeGroupName(ctx, default, node_name):
2004
  """Gets group name of an instance node.
2005

2006
  @type ctx: L{InstanceQueryData}
2007
  @param default: Default value
2008
  @type node_name: string
2009
  @param node_name: Node name
2010

2011
  """
2012
  try:
2013
    node = ctx.nodes[node_name]
2014
  except KeyError:
2015
    return default
2016

    
2017
  try:
2018
    group = ctx.groups[node.group]
2019
  except KeyError:
2020
    return default
2021

    
2022
  return group.name
2023

    
2024

    
2025
def _BuildInstanceFields():
2026
  """Builds list of fields for instance queries.
2027

2028
  """
2029
  fields = [
2030
    (_MakeField("pnode", "Primary_node", QFT_TEXT, "Primary node"),
2031
     IQ_CONFIG, QFF_HOSTNAME, _GetItemAttr("primary_node")),
2032
    (_MakeField("pnode.group", "PrimaryNodeGroup", QFT_TEXT,
2033
                "Primary node's group"),
2034
     IQ_NODES, 0,
2035
     lambda ctx, inst: _GetInstNodeGroupName(ctx, _FS_UNAVAIL,
2036
                                             inst.primary_node)),
2037
    (_MakeField("pnode.group.uuid", "PrimaryNodeGroupUUID", QFT_TEXT,
2038
                "Primary node's group UUID"),
2039
     IQ_NODES, 0,
2040
     lambda ctx, inst: _GetInstNodeGroup(ctx, _FS_UNAVAIL, inst.primary_node)),
2041
    # TODO: Allow filtering by secondary node as hostname
2042
    (_MakeField("snodes", "Secondary_Nodes", QFT_OTHER,
2043
                "Secondary nodes; usually this will just be one node"),
2044
     IQ_CONFIG, 0, lambda ctx, inst: list(inst.secondary_nodes)),
2045
    (_MakeField("snodes.group", "SecondaryNodesGroups", QFT_OTHER,
2046
                "Node groups of secondary nodes"),
2047
     IQ_NODES, 0,
2048
     lambda ctx, inst: map(compat.partial(_GetInstNodeGroupName, ctx, None),
2049
                           inst.secondary_nodes)),
2050
    (_MakeField("snodes.group.uuid", "SecondaryNodesGroupsUUID", QFT_OTHER,
2051
                "Node group UUIDs of secondary nodes"),
2052
     IQ_NODES, 0,
2053
     lambda ctx, inst: map(compat.partial(_GetInstNodeGroup, ctx, None),
2054
                           inst.secondary_nodes)),
2055
    (_MakeField("admin_state", "InstanceState", QFT_TEXT,
2056
                "Desired state of instance"),
2057
     IQ_CONFIG, 0, _GetItemAttr("admin_state")),
2058
    (_MakeField("admin_up", "Autostart", QFT_BOOL,
2059
                "Desired state of instance"),
2060
     IQ_CONFIG, 0, lambda ctx, inst: inst.admin_state == constants.ADMINST_UP),
2061
    (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), IQ_CONFIG, 0,
2062
     lambda ctx, inst: list(inst.GetTags())),
2063
    (_MakeField("console", "Console", QFT_OTHER,
2064
                "Instance console information"), IQ_CONSOLE, 0,
2065
     _GetInstanceConsole),
2066
    ]
2067

    
2068
  # Add simple fields
2069
  fields.extend([
2070
    (_MakeField(name, title, kind, doc), IQ_CONFIG, flags, _GetItemAttr(name))
2071
    for (name, (title, kind, flags, doc)) in _INST_SIMPLE_FIELDS.items()])
2072

    
2073
  # Fields requiring talking to the node
2074
  fields.extend([
2075
    (_MakeField("oper_state", "Running", QFT_BOOL, "Actual state of instance"),
2076
     IQ_LIVE, 0, _GetInstOperState),
2077
    (_MakeField("oper_ram", "Memory", QFT_UNIT,
2078
                "Actual memory usage as seen by hypervisor"),
2079
     IQ_LIVE, 0, _GetInstLiveData("memory")),
2080
    (_MakeField("oper_vcpus", "VCPUs", QFT_NUMBER,
2081
                "Actual number of VCPUs as seen by hypervisor"),
2082
     IQ_LIVE, 0, _GetInstLiveData("vcpus")),
2083
    ])
2084

    
2085
  # Status field
2086
  status_values = (constants.INSTST_RUNNING, constants.INSTST_ADMINDOWN,
2087
                   constants.INSTST_WRONGNODE, constants.INSTST_ERRORUP,
2088
                   constants.INSTST_ERRORDOWN, constants.INSTST_NODEDOWN,
2089
                   constants.INSTST_NODEOFFLINE, constants.INSTST_ADMINOFFLINE)
2090
  status_doc = ("Instance status; \"%s\" if instance is set to be running"
2091
                " and actually is, \"%s\" if instance is stopped and"
2092
                " is not running, \"%s\" if instance running, but not on its"
2093
                " designated primary node, \"%s\" if instance should be"
2094
                " stopped, but is actually running, \"%s\" if instance should"
2095
                " run, but doesn't, \"%s\" if instance's primary node is down,"
2096
                " \"%s\" if instance's primary node is marked offline,"
2097
                " \"%s\" if instance is offline and does not use dynamic"
2098
                " resources" % status_values)
2099
  fields.append((_MakeField("status", "Status", QFT_TEXT, status_doc),
2100
                 IQ_LIVE, 0, _GetInstStatus))
2101
  assert set(status_values) == constants.INSTST_ALL, \
2102
         "Status documentation mismatch"
2103

    
2104
  (network_fields, network_aliases) = _GetInstanceNetworkFields()
2105

    
2106
  fields.extend(network_fields)
2107
  fields.extend(_GetInstanceParameterFields())
2108
  fields.extend(_GetInstanceDiskFields())
2109
  fields.extend(_GetItemTimestampFields(IQ_CONFIG))
2110

    
2111
  aliases = [
2112
    ("vcpus", "be/vcpus"),
2113
    ("be/memory", "be/maxmem"),
2114
    ("sda_size", "disk.size/0"),
2115
    ("sdb_size", "disk.size/1"),
2116
    ] + network_aliases
2117

    
2118
  return _PrepareFieldList(fields, aliases)
2119

    
2120

    
2121
class LockQueryData:
2122
  """Data container for lock data queries.
2123

2124
  """
2125
  def __init__(self, lockdata):
2126
    """Initializes this class.
2127

2128
    """
2129
    self.lockdata = lockdata
2130

    
2131
  def __iter__(self):
2132
    """Iterate over all locks.
2133

2134
    """
2135
    return iter(self.lockdata)
2136

    
2137

    
2138
def _GetLockOwners(_, data):
2139
  """Returns a sorted list of a lock's current owners.
2140

2141
  """
2142
  (_, _, owners, _) = data
2143

    
2144
  if owners:
2145
    owners = utils.NiceSort(owners)
2146

    
2147
  return owners
2148

    
2149

    
2150
def _GetLockPending(_, data):
2151
  """Returns a sorted list of a lock's pending acquires.
2152

2153
  """
2154
  (_, _, _, pending) = data
2155

    
2156
  if pending:
2157
    pending = [(mode, utils.NiceSort(names))
2158
               for (mode, names) in pending]
2159

    
2160
  return pending
2161

    
2162

    
2163
def _BuildLockFields():
2164
  """Builds list of fields for lock queries.
2165

2166
  """
2167
  return _PrepareFieldList([
2168
    # TODO: Lock names are not always hostnames. Should QFF_HOSTNAME be used?
2169
    (_MakeField("name", "Name", QFT_TEXT, "Lock name"), None, 0,
2170
     lambda ctx, (name, mode, owners, pending): name),
2171
    (_MakeField("mode", "Mode", QFT_OTHER,
2172
                "Mode in which the lock is currently acquired"
2173
                " (exclusive or shared)"),
2174
     LQ_MODE, 0, lambda ctx, (name, mode, owners, pending): mode),
2175
    (_MakeField("owner", "Owner", QFT_OTHER, "Current lock owner(s)"),
2176
     LQ_OWNER, 0, _GetLockOwners),
2177
    (_MakeField("pending", "Pending", QFT_OTHER,
2178
                "Threads waiting for the lock"),
2179
     LQ_PENDING, 0, _GetLockPending),
2180
    ], [])
2181

    
2182

    
2183
class GroupQueryData:
2184
  """Data container for node group data queries.
2185

2186
  """
2187
  def __init__(self, cluster, groups, group_to_nodes, group_to_instances,
2188
               want_diskparams):
2189
    """Initializes this class.
2190

2191
    @param cluster: Cluster object
2192
    @param groups: List of node group objects
2193
    @type group_to_nodes: dict; group UUID as key
2194
    @param group_to_nodes: Per-group list of nodes
2195
    @type group_to_instances: dict; group UUID as key
2196
    @param group_to_instances: Per-group list of (primary) instances
2197
    @type want_diskparams: bool
2198
    @param want_diskparams: Whether diskparamters should be calculated
2199

2200
    """
2201
    self.groups = groups
2202
    self.group_to_nodes = group_to_nodes
2203
    self.group_to_instances = group_to_instances
2204
    self.cluster = cluster
2205
    self.want_diskparams = want_diskparams
2206

    
2207
    # Used for individual rows
2208
    self.group_ipolicy = None
2209
    self.ndparams = None
2210
    self.group_dp = None
2211

    
2212
  def __iter__(self):
2213
    """Iterate over all node groups.
2214

2215
    This function has side-effects and only one instance of the resulting
2216
    generator should be used at a time.
2217

2218
    """
2219
    for group in self.groups:
2220
      self.group_ipolicy = self.cluster.SimpleFillIPolicy(group.ipolicy)
2221
      self.ndparams = self.cluster.SimpleFillND(group.ndparams)
2222
      if self.want_diskparams:
2223
        self.group_dp = self.cluster.SimpleFillDP(group.diskparams)
2224
      else:
2225
        self.group_dp = None
2226
      yield group
2227

    
2228

    
2229
_GROUP_SIMPLE_FIELDS = {
2230
  "alloc_policy": ("AllocPolicy", QFT_TEXT, "Allocation policy for group"),
2231
  "name": ("Group", QFT_TEXT, "Group name"),
2232
  "serial_no": ("SerialNo", QFT_NUMBER, _SERIAL_NO_DOC % "Group"),
2233
  "uuid": ("UUID", QFT_TEXT, "Group UUID"),
2234
  }
2235

    
2236

    
2237
def _BuildGroupFields():
2238
  """Builds list of fields for node group queries.
2239

2240
  """
2241
  # Add simple fields
2242
  fields = [(_MakeField(name, title, kind, doc), GQ_CONFIG, 0,
2243
             _GetItemAttr(name))
2244
            for (name, (title, kind, doc)) in _GROUP_SIMPLE_FIELDS.items()]
2245

    
2246
  def _GetLength(getter):
2247
    return lambda ctx, group: len(getter(ctx)[group.uuid])
2248

    
2249
  def _GetSortedList(getter):
2250
    return lambda ctx, group: utils.NiceSort(getter(ctx)[group.uuid])
2251

    
2252
  group_to_nodes = operator.attrgetter("group_to_nodes")
2253
  group_to_instances = operator.attrgetter("group_to_instances")
2254

    
2255
  # Add fields for nodes
2256
  fields.extend([
2257
    (_MakeField("node_cnt", "Nodes", QFT_NUMBER, "Number of nodes"),
2258
     GQ_NODE, 0, _GetLength(group_to_nodes)),
2259
    (_MakeField("node_list", "NodeList", QFT_OTHER, "List of nodes"),
2260
     GQ_NODE, 0, _GetSortedList(group_to_nodes)),
2261
    ])
2262

    
2263
  # Add fields for instances
2264
  fields.extend([
2265
    (_MakeField("pinst_cnt", "Instances", QFT_NUMBER,
2266
                "Number of primary instances"),
2267
     GQ_INST, 0, _GetLength(group_to_instances)),
2268
    (_MakeField("pinst_list", "InstanceList", QFT_OTHER,
2269
                "List of primary instances"),
2270
     GQ_INST, 0, _GetSortedList(group_to_instances)),
2271
    ])
2272

    
2273
  # Other fields
2274
  fields.extend([
2275
    (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), GQ_CONFIG, 0,
2276
     lambda ctx, group: list(group.GetTags())),
2277
    (_MakeField("ipolicy", "InstancePolicy", QFT_OTHER,
2278
                "Instance policy limitations (merged)"),
2279
     GQ_CONFIG, 0, lambda ctx, _: ctx.group_ipolicy),
2280
    (_MakeField("custom_ipolicy", "CustomInstancePolicy", QFT_OTHER,
2281
                "Custom instance policy limitations"),
2282
     GQ_CONFIG, 0, _GetItemAttr("ipolicy")),
2283
    (_MakeField("custom_ndparams", "CustomNDParams", QFT_OTHER,
2284
                "Custom node parameters"),
2285
     GQ_CONFIG, 0, _GetItemAttr("ndparams")),
2286
    (_MakeField("ndparams", "NDParams", QFT_OTHER,
2287
                "Node parameters"),
2288
     GQ_CONFIG, 0, lambda ctx, _: ctx.ndparams),
2289
    (_MakeField("diskparams", "DiskParameters", QFT_OTHER,
2290
                "Disk parameters (merged)"),
2291
     GQ_DISKPARAMS, 0, lambda ctx, _: ctx.group_dp),
2292
    (_MakeField("custom_diskparams", "CustomDiskParameters", QFT_OTHER,
2293
                "Custom disk parameters"),
2294
     GQ_CONFIG, 0, _GetItemAttr("diskparams")),
2295
    ])
2296

    
2297
  # ND parameters
2298
  fields.extend(_BuildNDFields(True))
2299

    
2300
  fields.extend(_GetItemTimestampFields(GQ_CONFIG))
2301

    
2302
  return _PrepareFieldList(fields, [])
2303

    
2304

    
2305
class OsInfo(objects.ConfigObject):
2306
  __slots__ = [
2307
    "name",
2308
    "valid",
2309
    "hidden",
2310
    "blacklisted",
2311
    "variants",
2312
    "api_versions",
2313
    "parameters",
2314
    "node_status",
2315
    ]
2316

    
2317

    
2318
def _BuildOsFields():
2319
  """Builds list of fields for operating system queries.
2320

2321
  """
2322
  fields = [
2323
    (_MakeField("name", "Name", QFT_TEXT, "Operating system name"),
2324
     None, 0, _GetItemAttr("name")),
2325
    (_MakeField("valid", "Valid", QFT_BOOL,
2326
                "Whether operating system definition is valid"),
2327
     None, 0, _GetItemAttr("valid")),
2328
    (_MakeField("hidden", "Hidden", QFT_BOOL,
2329
                "Whether operating system is hidden"),
2330
     None, 0, _GetItemAttr("hidden")),
2331
    (_MakeField("blacklisted", "Blacklisted", QFT_BOOL,
2332
                "Whether operating system is blacklisted"),
2333
     None, 0, _GetItemAttr("blacklisted")),
2334
    (_MakeField("variants", "Variants", QFT_OTHER,
2335
                "Operating system variants"),
2336
     None, 0, _ConvWrap(utils.NiceSort, _GetItemAttr("variants"))),
2337
    (_MakeField("api_versions", "ApiVersions", QFT_OTHER,
2338
                "Operating system API versions"),
2339
     None, 0, _ConvWrap(sorted, _GetItemAttr("api_versions"))),
2340
    (_MakeField("parameters", "Parameters", QFT_OTHER,
2341
                "Operating system parameters"),
2342
     None, 0, _ConvWrap(compat.partial(utils.NiceSort, key=compat.fst),
2343
                        _GetItemAttr("parameters"))),
2344
    (_MakeField("node_status", "NodeStatus", QFT_OTHER,
2345
                "Status from node"),
2346
     None, 0, _GetItemAttr("node_status")),
2347
    ]
2348

    
2349
  return _PrepareFieldList(fields, [])
2350

    
2351

    
2352
class ExtStorageInfo(objects.ConfigObject):
2353
  __slots__ = [
2354
    "name",
2355
    "node_status",
2356
    "nodegroup_status",
2357
    "parameters",
2358
    ]
2359

    
2360

    
2361
def _BuildExtStorageFields():
2362
  """Builds list of fields for extstorage provider queries.
2363

2364
  """
2365
  fields = [
2366
    (_MakeField("name", "Name", QFT_TEXT, "ExtStorage provider name"),
2367
     None, 0, _GetItemAttr("name")),
2368
    (_MakeField("node_status", "NodeStatus", QFT_OTHER,
2369
                "Status from node"),
2370
     None, 0, _GetItemAttr("node_status")),
2371
    (_MakeField("nodegroup_status", "NodegroupStatus", QFT_OTHER,
2372
                "Overall Nodegroup status"),
2373
     None, 0, _GetItemAttr("nodegroup_status")),
2374
    (_MakeField("parameters", "Parameters", QFT_OTHER,
2375
                "ExtStorage provider parameters"),
2376
     None, 0, _GetItemAttr("parameters")),
2377
    ]
2378

    
2379
  return _PrepareFieldList(fields, [])
2380

    
2381

    
2382
def _JobUnavailInner(fn, ctx, (job_id, job)): # pylint: disable=W0613
2383
  """Return L{_FS_UNAVAIL} if job is None.
2384

2385
  When listing specifc jobs (e.g. "gnt-job list 1 2 3"), a job may not be
2386
  found, in which case this function converts it to L{_FS_UNAVAIL}.
2387

2388
  """
2389
  if job is None:
2390
    return _FS_UNAVAIL
2391
  else:
2392
    return fn(job)
2393

    
2394

    
2395
def _JobUnavail(inner):
2396
  """Wrapper for L{_JobUnavailInner}.
2397

2398
  """
2399
  return compat.partial(_JobUnavailInner, inner)
2400

    
2401

    
2402
def _PerJobOpInner(fn, job):
2403
  """Executes a function per opcode in a job.
2404

2405
  """
2406
  return map(fn, job.ops)
2407

    
2408

    
2409
def _PerJobOp(fn):
2410
  """Wrapper for L{_PerJobOpInner}.
2411

2412
  """
2413
  return _JobUnavail(compat.partial(_PerJobOpInner, fn))
2414

    
2415

    
2416
def _JobTimestampInner(fn, job):
2417
  """Converts unavailable timestamp to L{_FS_UNAVAIL}.
2418

2419
  """
2420
  timestamp = fn(job)
2421

    
2422
  if timestamp is None:
2423
    return _FS_UNAVAIL
2424
  else:
2425
    return timestamp
2426

    
2427

    
2428
def _JobTimestamp(fn):
2429
  """Wrapper for L{_JobTimestampInner}.
2430

2431
  """
2432
  return _JobUnavail(compat.partial(_JobTimestampInner, fn))
2433

    
2434

    
2435
def _BuildJobFields():
2436
  """Builds list of fields for job queries.
2437

2438
  """
2439
  fields = [
2440
    (_MakeField("id", "ID", QFT_NUMBER, "Job ID"),
2441
     None, QFF_JOB_ID, lambda _, (job_id, job): job_id),
2442
    (_MakeField("status", "Status", QFT_TEXT, "Job status"),
2443
     None, 0, _JobUnavail(lambda job: job.CalcStatus())),
2444
    (_MakeField("priority", "Priority", QFT_NUMBER,
2445
                ("Current job priority (%s to %s)" %
2446
                 (constants.OP_PRIO_LOWEST, constants.OP_PRIO_HIGHEST))),
2447
     None, 0, _JobUnavail(lambda job: job.CalcPriority())),
2448
    (_MakeField("archived", "Archived", QFT_BOOL, "Whether job is archived"),
2449
     JQ_ARCHIVED, 0, lambda _, (job_id, job): job.archived),
2450
    (_MakeField("ops", "OpCodes", QFT_OTHER, "List of all opcodes"),
2451
     None, 0, _PerJobOp(lambda op: op.input.__getstate__())),
2452
    (_MakeField("opresult", "OpCode_result", QFT_OTHER,
2453
                "List of opcodes results"),
2454
     None, 0, _PerJobOp(operator.attrgetter("result"))),
2455
    (_MakeField("opstatus", "OpCode_status", QFT_OTHER,
2456
                "List of opcodes status"),
2457
     None, 0, _PerJobOp(operator.attrgetter("status"))),
2458
    (_MakeField("oplog", "OpCode_log", QFT_OTHER,
2459
                "List of opcode output logs"),
2460
     None, 0, _PerJobOp(operator.attrgetter("log"))),
2461
    (_MakeField("opstart", "OpCode_start", QFT_OTHER,
2462
                "List of opcode start timestamps (before acquiring locks)"),
2463
     None, 0, _PerJobOp(operator.attrgetter("start_timestamp"))),
2464
    (_MakeField("opexec", "OpCode_exec", QFT_OTHER,
2465
                "List of opcode execution start timestamps (after acquiring"
2466
                " locks)"),
2467
     None, 0, _PerJobOp(operator.attrgetter("exec_timestamp"))),
2468
    (_MakeField("opend", "OpCode_end", QFT_OTHER,
2469
                "List of opcode execution end timestamps"),
2470
     None, 0, _PerJobOp(operator.attrgetter("end_timestamp"))),
2471
    (_MakeField("oppriority", "OpCode_prio", QFT_OTHER,
2472
                "List of opcode priorities"),
2473
     None, 0, _PerJobOp(operator.attrgetter("priority"))),
2474
    (_MakeField("summary", "Summary", QFT_OTHER,
2475
                "List of per-opcode summaries"),
2476
     None, 0, _PerJobOp(lambda op: op.input.Summary())),
2477
    ]
2478

    
2479
  # Timestamp fields
2480
  for (name, attr, title, desc) in [
2481
    ("received_ts", "received_timestamp", "Received",
2482
     "Timestamp of when job was received"),
2483
    ("start_ts", "start_timestamp", "Start", "Timestamp of job start"),
2484
    ("end_ts", "end_timestamp", "End", "Timestamp of job end"),
2485
    ]:
2486
    getter = operator.attrgetter(attr)
2487
    fields.extend([
2488
      (_MakeField(name, title, QFT_OTHER,
2489
                  "%s (tuple containing seconds and microseconds)" % desc),
2490
       None, QFF_SPLIT_TIMESTAMP, _JobTimestamp(getter)),
2491
      ])
2492

    
2493
  return _PrepareFieldList(fields, [])
2494

    
2495

    
2496
def _GetExportName(_, (node_name, expname)): # pylint: disable=W0613
2497
  """Returns an export name if available.
2498

2499
  """
2500
  if expname is None:
2501
    return _FS_UNAVAIL
2502
  else:
2503
    return expname
2504

    
2505

    
2506
def _BuildExportFields():
2507
  """Builds list of fields for exports.
2508

2509
  """
2510
  fields = [
2511
    (_MakeField("node", "Node", QFT_TEXT, "Node name"),
2512
     None, QFF_HOSTNAME, lambda _, (node_name, expname): node_name),
2513
    (_MakeField("export", "Export", QFT_TEXT, "Export name"),
2514
     None, 0, _GetExportName),
2515
    ]
2516

    
2517
  return _PrepareFieldList(fields, [])
2518

    
2519

    
2520
_CLUSTER_VERSION_FIELDS = {
2521
  "software_version": ("SoftwareVersion", QFT_TEXT, constants.RELEASE_VERSION,
2522
                       "Software version"),
2523
  "protocol_version": ("ProtocolVersion", QFT_NUMBER,
2524
                       constants.PROTOCOL_VERSION,
2525
                       "RPC protocol version"),
2526
  "config_version": ("ConfigVersion", QFT_NUMBER, constants.CONFIG_VERSION,
2527
                     "Configuration format version"),
2528
  "os_api_version": ("OsApiVersion", QFT_NUMBER, max(constants.OS_API_VERSIONS),
2529
                     "API version for OS template scripts"),
2530
  "export_version": ("ExportVersion", QFT_NUMBER, constants.EXPORT_VERSION,
2531
                     "Import/export file format version"),
2532
  }
2533

    
2534

    
2535
_CLUSTER_SIMPLE_FIELDS = {
2536
  "cluster_name": ("Name", QFT_TEXT, QFF_HOSTNAME, "Cluster name"),
2537
  "master_node": ("Master", QFT_TEXT, QFF_HOSTNAME, "Master node name"),
2538
  "volume_group_name": ("VgName", QFT_TEXT, 0, "LVM volume group name"),
2539
  }
2540

    
2541

    
2542
class ClusterQueryData:
2543
  def __init__(self, cluster, drain_flag, watcher_pause):
2544
    """Initializes this class.
2545

2546
    @type cluster: L{objects.Cluster}
2547
    @param cluster: Instance of cluster object
2548
    @type drain_flag: bool
2549
    @param drain_flag: Whether job queue is drained
2550
    @type watcher_pause: number
2551
    @param watcher_pause: Until when watcher is paused (Unix timestamp)
2552

2553
    """
2554
    self._cluster = cluster
2555
    self.drain_flag = drain_flag
2556
    self.watcher_pause = watcher_pause
2557

    
2558
  def __iter__(self):
2559
    return iter([self._cluster])
2560

    
2561

    
2562
def _ClusterWatcherPause(ctx, _):
2563
  """Returns until when watcher is paused (if available).
2564

2565
  """
2566
  if ctx.watcher_pause is None:
2567
    return _FS_UNAVAIL
2568
  else:
2569
    return ctx.watcher_pause
2570

    
2571

    
2572
def _BuildClusterFields():
2573
  """Builds list of fields for cluster information.
2574

2575
  """
2576
  fields = [
2577
    (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), CQ_CONFIG, 0,
2578
     lambda ctx, cluster: list(cluster.GetTags())),
2579
    (_MakeField("architecture", "ArchInfo", QFT_OTHER,
2580
                "Architecture information"), None, 0,
2581
     lambda ctx, _: runtime.GetArchInfo()),
2582
    (_MakeField("drain_flag", "QueueDrained", QFT_BOOL,
2583
                "Flag whether job queue is drained"), CQ_QUEUE_DRAINED, 0,
2584
     lambda ctx, _: ctx.drain_flag),
2585
    (_MakeField("watcher_pause", "WatcherPause", QFT_TIMESTAMP,
2586
                "Until when watcher is paused"), CQ_WATCHER_PAUSE, 0,
2587
     _ClusterWatcherPause),
2588
    ]
2589

    
2590
  # Simple fields
2591
  fields.extend([
2592
    (_MakeField(name, title, kind, doc), CQ_CONFIG, flags, _GetItemAttr(name))
2593
    for (name, (title, kind, flags, doc)) in _CLUSTER_SIMPLE_FIELDS.items()
2594
    ],)
2595

    
2596
  # Version fields
2597
  fields.extend([
2598
    (_MakeField(name, title, kind, doc), None, 0, _StaticValue(value))
2599
    for (name, (title, kind, value, doc)) in _CLUSTER_VERSION_FIELDS.items()])
2600

    
2601
  # Add timestamps
2602
  fields.extend(_GetItemTimestampFields(CQ_CONFIG))
2603

    
2604
  return _PrepareFieldList(fields, [
2605
    ("name", "cluster_name")])
2606

    
2607

    
2608
class NetworkQueryData:
2609
  """Data container for network data queries.
2610

2611
  """
2612
  def __init__(self, networks, network_to_groups,
2613
               network_to_instances, stats):
2614
    """Initializes this class.
2615

2616
    @param networks: List of network objects
2617
    @type network_to_groups: dict; network UUID as key
2618
    @param network_to_groups: Per-network list of groups
2619
    @type network_to_instances: dict; network UUID as key
2620
    @param network_to_instances: Per-network list of instances
2621
    @type stats: dict; network UUID as key
2622
    @param stats: Per-network usage statistics
2623

2624
    """
2625
    self.networks = networks
2626
    self.network_to_groups = network_to_groups
2627
    self.network_to_instances = network_to_instances
2628
    self.stats = stats
2629

    
2630
  def __iter__(self):
2631
    """Iterate over all networks.
2632

2633
    """
2634
    for net in self.networks:
2635
      if self.stats:
2636
        self.curstats = self.stats.get(net.uuid, None)
2637
      else:
2638
        self.curstats = None
2639
      yield net
2640

    
2641

    
2642
_NETWORK_SIMPLE_FIELDS = {
2643
  "name": ("Network", QFT_TEXT, 0, "Name"),
2644
  "network": ("Subnet", QFT_OTHER, 0, "IPv4 subnet"),
2645
  "gateway": ("Gateway", QFT_OTHER, 0, "IPv4 gateway"),
2646
  "network6": ("IPv6Subnet", QFT_OTHER, 0, "IPv6 subnet"),
2647
  "gateway6": ("IPv6Gateway", QFT_OTHER, 0, "IPv6 gateway"),
2648
  "mac_prefix": ("MacPrefix", QFT_OTHER, 0, "MAC address prefix"),
2649
  "serial_no": ("SerialNo", QFT_NUMBER, 0, _SERIAL_NO_DOC % "Network"),
2650
  "uuid": ("UUID", QFT_TEXT, 0, "Network UUID"),
2651
  }
2652

    
2653

    
2654
_NETWORK_STATS_FIELDS = {
2655
  "free_count": ("FreeCount", QFT_NUMBER, 0, "Number of available addresses"),
2656
  "reserved_count":
2657
    ("ReservedCount", QFT_NUMBER, 0, "Number of reserved addresses"),
2658
  "map": ("Map", QFT_TEXT, 0, "Actual mapping"),
2659
  "external_reservations":
2660
    ("ExternalReservations", QFT_TEXT, 0, "External reservations"),
2661
  }
2662

    
2663

    
2664
def _GetNetworkStatsField(field, kind, ctx, _):
2665
  """Gets the value of a "stats" field from L{NetworkQueryData}.
2666

2667
  @param field: Field name
2668
  @param kind: Data kind, one of L{constants.QFT_ALL}
2669
  @type ctx: L{NetworkQueryData}
2670

2671
  """
2672
  return _GetStatsField(field, kind, ctx.curstats)
2673

    
2674

    
2675
def _BuildNetworkFields():
2676
  """Builds list of fields for network queries.
2677

2678
  """
2679
  fields = [
2680
    (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), IQ_CONFIG, 0,
2681
     lambda ctx, inst: list(inst.GetTags())),
2682
    ]
2683

    
2684
  # Add simple fields
2685
  fields.extend([
2686
    (_MakeField(name, title, kind, doc),
2687
     NETQ_CONFIG, 0, _GetItemAttr(name))
2688
     for (name, (title, kind, _, doc)) in _NETWORK_SIMPLE_FIELDS.items()])
2689

    
2690
  def _GetLength(getter):
2691
    return lambda ctx, network: len(getter(ctx)[network.uuid])
2692

    
2693
  def _GetSortedList(getter):
2694
    return lambda ctx, network: utils.NiceSort(getter(ctx)[network.uuid])
2695

    
2696
  network_to_groups = operator.attrgetter("network_to_groups")
2697
  network_to_instances = operator.attrgetter("network_to_instances")
2698

    
2699
  # Add fields for node groups
2700
  fields.extend([
2701
    (_MakeField("group_cnt", "NodeGroups", QFT_NUMBER, "Number of nodegroups"),
2702
     NETQ_GROUP, 0, _GetLength(network_to_groups)),
2703
    (_MakeField("group_list", "GroupList", QFT_OTHER,
2704
     "List of nodegroups (group name, NIC mode, NIC link)"),
2705
     NETQ_GROUP, 0, lambda ctx, network: network_to_groups(ctx)[network.uuid]),
2706
    ])
2707

    
2708
  # Add fields for instances
2709
  fields.extend([
2710
    (_MakeField("inst_cnt", "Instances", QFT_NUMBER, "Number of instances"),
2711
     NETQ_INST, 0, _GetLength(network_to_instances)),
2712
    (_MakeField("inst_list", "InstanceList", QFT_OTHER, "List of instances"),
2713
     NETQ_INST, 0, _GetSortedList(network_to_instances)),
2714
    ])
2715

    
2716
  # Add fields for usage statistics
2717
  fields.extend([
2718
    (_MakeField(name, title, kind, doc), NETQ_STATS, 0,
2719
    compat.partial(_GetNetworkStatsField, name, kind))
2720
    for (name, (title, kind, _, doc)) in _NETWORK_STATS_FIELDS.items()])
2721

    
2722
  return _PrepareFieldList(fields, [])
2723

    
2724
#: Fields for cluster information
2725
CLUSTER_FIELDS = _BuildClusterFields()
2726

    
2727
#: Fields available for node queries
2728
NODE_FIELDS = _BuildNodeFields()
2729

    
2730
#: Fields available for instance queries
2731
INSTANCE_FIELDS = _BuildInstanceFields()
2732

    
2733
#: Fields available for lock queries
2734
LOCK_FIELDS = _BuildLockFields()
2735

    
2736
#: Fields available for node group queries
2737
GROUP_FIELDS = _BuildGroupFields()
2738

    
2739
#: Fields available for operating system queries
2740
OS_FIELDS = _BuildOsFields()
2741

    
2742
#: Fields available for extstorage provider queries
2743
EXTSTORAGE_FIELDS = _BuildExtStorageFields()
2744

    
2745
#: Fields available for job queries
2746
JOB_FIELDS = _BuildJobFields()
2747

    
2748
#: Fields available for exports
2749
EXPORT_FIELDS = _BuildExportFields()
2750

    
2751
#: Fields available for network queries
2752
NETWORK_FIELDS = _BuildNetworkFields()
2753

    
2754
#: All available resources
2755
ALL_FIELDS = {
2756
  constants.QR_CLUSTER: CLUSTER_FIELDS,
2757
  constants.QR_INSTANCE: INSTANCE_FIELDS,
2758
  constants.QR_NODE: NODE_FIELDS,
2759
  constants.QR_LOCK: LOCK_FIELDS,
2760
  constants.QR_GROUP: GROUP_FIELDS,
2761
  constants.QR_OS: OS_FIELDS,
2762
  constants.QR_EXTSTORAGE: EXTSTORAGE_FIELDS,
2763
  constants.QR_JOB: JOB_FIELDS,
2764
  constants.QR_EXPORT: EXPORT_FIELDS,
2765
  constants.QR_NETWORK: NETWORK_FIELDS,
2766
  }
2767

    
2768
#: All available field lists
2769
ALL_FIELD_LISTS = ALL_FIELDS.values()