Statistics
| Branch: | Tag: | Revision:

root / lib / query.py @ 1c8addc6

History | View | Annotate | Download (27.2 kB)

1
#
2
#
3

    
4
# Copyright (C) 2010 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
import logging
25
import operator
26
import re
27

    
28
from ganeti import constants
29
from ganeti import errors
30
from ganeti import utils
31
from ganeti import compat
32
from ganeti import objects
33
from ganeti import ht
34

    
35

    
36
(NQ_CONFIG,
37
 NQ_INST,
38
 NQ_LIVE,
39
 NQ_GROUP) = range(1, 5)
40

    
41
(IQ_CONFIG,
42
 IQ_LIVE,
43
 IQ_DISKUSAGE) = range(100, 103)
44

    
45

    
46
FIELD_NAME_RE = re.compile(r"^[a-z0-9/._]+$")
47
TITLE_RE = re.compile(r"^[^\s]+$")
48

    
49
#: Verification function for each field type
50
_VERIFY_FN = {
51
  constants.QFT_UNKNOWN: ht.TNone,
52
  constants.QFT_TEXT: ht.TString,
53
  constants.QFT_BOOL: ht.TBool,
54
  constants.QFT_NUMBER: ht.TInt,
55
  constants.QFT_UNIT: ht.TInt,
56
  constants.QFT_TIMESTAMP: ht.TOr(ht.TInt, ht.TFloat),
57
  constants.QFT_OTHER: lambda _: True,
58
  }
59

    
60

    
61
def _GetUnknownField(ctx, item): # pylint: disable-msg=W0613
62
  """Gets the contents of an unknown field.
63

64
  """
65
  return (constants.QRFS_UNKNOWN, None)
66

    
67

    
68
def _GetQueryFields(fielddefs, selected):
69
  """Calculates the internal list of selected fields.
70

71
  Unknown fields are returned as L{constants.QFT_UNKNOWN}.
72

73
  @type fielddefs: dict
74
  @param fielddefs: Field definitions
75
  @type selected: list of strings
76
  @param selected: List of selected fields
77

78
  """
79
  result = []
80

    
81
  for name in selected:
82
    try:
83
      fdef = fielddefs[name]
84
    except KeyError:
85
      fdef = (_MakeField(name, name, constants.QFT_UNKNOWN),
86
              None, _GetUnknownField)
87

    
88
    assert len(fdef) == 3
89

    
90
    result.append(fdef)
91

    
92
  return result
93

    
94

    
95
def GetAllFields(fielddefs):
96
  """Extract L{objects.QueryFieldDefinition} from field definitions.
97

98
  @rtype: list of L{objects.QueryFieldDefinition}
99

100
  """
101
  return [fdef for (fdef, _, _) in fielddefs]
102

    
103

    
104
class Query:
105
  def __init__(self, fieldlist, selected):
106
    """Initializes this class.
107

108
    The field definition is a dictionary with the field's name as a key and a
109
    tuple containing, in order, the field definition object
110
    (L{objects.QueryFieldDefinition}, the data kind to help calling code
111
    collect data and a retrieval function. The retrieval function is called
112
    with two parameters, in order, the data container and the item in container
113
    (see L{Query.Query}).
114

115
    Users of this class can call L{RequestedData} before preparing the data
116
    container to determine what data is needed.
117

118
    @type fieldlist: dictionary
119
    @param fieldlist: Field definitions
120
    @type selected: list of strings
121
    @param selected: List of selected fields
122

123
    """
124
    self._fields = _GetQueryFields(fieldlist, selected)
125

    
126
  def RequestedData(self):
127
    """Gets requested kinds of data.
128

129
    @rtype: frozenset
130

131
    """
132
    return frozenset(datakind
133
                     for (_, datakind, _) in self._fields
134
                     if datakind is not None)
135

    
136
  def GetFields(self):
137
    """Returns the list of fields for this query.
138

139
    Includes unknown fields.
140

141
    @rtype: List of L{objects.QueryFieldDefinition}
142

143
    """
144
    return GetAllFields(self._fields)
145

    
146
  def Query(self, ctx):
147
    """Execute a query.
148

149
    @param ctx: Data container passed to field retrieval functions, must
150
      support iteration using C{__iter__}
151

152
    """
153
    result = [[fn(ctx, item) for (_, _, fn) in self._fields]
154
              for item in ctx]
155

    
156
    # Verify result
157
    if __debug__:
158
      for (idx, row) in enumerate(result):
159
        assert _VerifyResultRow(self._fields, row), \
160
               ("Inconsistent result for fields %s in row %s: %r" %
161
                (GetAllFields(self._fields), idx, row))
162

    
163
    return result
164

    
165
  def OldStyleQuery(self, ctx):
166
    """Query with "old" query result format.
167

168
    See L{Query.Query} for arguments.
169

170
    """
171
    unknown = set(fdef.name
172
                  for (fdef, _, _) in self._fields
173
                  if fdef.kind == constants.QFT_UNKNOWN)
174
    if unknown:
175
      raise errors.OpPrereqError("Unknown output fields selected: %s" %
176
                                 (utils.CommaJoin(unknown), ),
177
                                 errors.ECODE_INVAL)
178

    
179
    return [[value for (_, value) in row]
180
            for row in self.Query(ctx)]
181

    
182

    
183
def _VerifyResultRow(fields, row):
184
  """Verifies the contents of a query result row.
185

186
  @type fields: list
187
  @param fields: Field definitions for result
188
  @type row: list of tuples
189
  @param row: Row data
190

191
  """
192
  return (len(row) == len(fields) and
193
          compat.all((status == constants.QRFS_NORMAL and
194
                      _VERIFY_FN[fdef.kind](value)) or
195
                     # Value for an abnormal status must be None
196
                     (status != constants.QRFS_NORMAL and value is None)
197
                     for ((status, value), (fdef, _, _)) in zip(row, fields)))
198

    
199

    
200
def _PrepareFieldList(fields):
201
  """Prepares field list for use by L{Query}.
202

203
  Converts the list to a dictionary and does some verification.
204

205
  @type fields: list of tuples; (L{objects.QueryFieldDefinition}, data kind,
206
    retrieval function)
207
  @param fields: List of fields
208
  @rtype: dict
209
  @return: Field dictionary for L{Query}
210

211
  """
212
  if __debug__:
213
    duplicates = utils.FindDuplicates(fdef.title.lower()
214
                                      for (fdef, _, _) in fields)
215
    assert not duplicates, "Duplicate title(s) found: %r" % duplicates
216

    
217
  result = {}
218

    
219
  for field in fields:
220
    (fdef, _, fn) = field
221

    
222
    assert fdef.name and fdef.title, "Name and title are required"
223
    assert FIELD_NAME_RE.match(fdef.name)
224
    assert TITLE_RE.match(fdef.title)
225
    assert callable(fn)
226
    assert fdef.name not in result, \
227
           "Duplicate field name '%s' found" % fdef.name
228

    
229
    result[fdef.name] = field
230

    
231
  assert len(result) == len(fields)
232
  assert compat.all(name == fdef.name
233
                    for (name, (fdef, _, _)) in result.items())
234

    
235
  return result
236

    
237

    
238
def _MakeField(name, title, kind):
239
  """Wrapper for creating L{objects.QueryFieldDefinition} instances.
240

241
  @param name: Field name as a regular expression
242
  @param title: Human-readable title
243
  @param kind: Field type
244

245
  """
246
  return objects.QueryFieldDefinition(name=name, title=title, kind=kind)
247

    
248

    
249
def _GetNodeRole(node, master_name):
250
  """Determine node role.
251

252
  @type node: L{objects.Node}
253
  @param node: Node object
254
  @type master_name: string
255
  @param master_name: Master node name
256

257
  """
258
  if node.name == master_name:
259
    return "M"
260
  elif node.master_candidate:
261
    return "C"
262
  elif node.drained:
263
    return "D"
264
  elif node.offline:
265
    return "O"
266
  else:
267
    return "R"
268

    
269

    
270
def _GetItemAttr(attr):
271
  """Returns a field function to return an attribute of the item.
272

273
  @param attr: Attribute name
274

275
  """
276
  getter = operator.attrgetter(attr)
277
  return lambda _, item: (constants.QRFS_NORMAL, getter(item))
278

    
279

    
280
class NodeQueryData:
281
  """Data container for node data queries.
282

283
  """
284
  def __init__(self, nodes, live_data, master_name, node_to_primary,
285
               node_to_secondary, groups):
286
    """Initializes this class.
287

288
    """
289
    self.nodes = nodes
290
    self.live_data = live_data
291
    self.master_name = master_name
292
    self.node_to_primary = node_to_primary
293
    self.node_to_secondary = node_to_secondary
294
    self.groups = groups
295

    
296
    # Used for individual rows
297
    self.curlive_data = None
298

    
299
  def __iter__(self):
300
    """Iterate over all nodes.
301

302
    This function has side-effects and only one instance of the resulting
303
    generator should be used at a time.
304

305
    """
306
    for node in self.nodes:
307
      if self.live_data:
308
        self.curlive_data = self.live_data.get(node.name, None)
309
      else:
310
        self.curlive_data = None
311
      yield node
312

    
313

    
314
#: Fields that are direct attributes of an L{objects.Node} object
315
_NODE_SIMPLE_FIELDS = {
316
  "ctime": ("CTime", constants.QFT_TIMESTAMP),
317
  "drained": ("Drained", constants.QFT_BOOL),
318
  "master_candidate": ("MasterC", constants.QFT_BOOL),
319
  "master_capable": ("MasterCapable", constants.QFT_BOOL),
320
  "mtime": ("MTime", constants.QFT_TIMESTAMP),
321
  "name": ("Node", constants.QFT_TEXT),
322
  "offline": ("Offline", constants.QFT_BOOL),
323
  "serial_no": ("SerialNo", constants.QFT_NUMBER),
324
  "uuid": ("UUID", constants.QFT_TEXT),
325
  "vm_capable": ("VMCapable", constants.QFT_BOOL),
326
  }
327

    
328

    
329
#: Fields requiring talking to the node
330
_NODE_LIVE_FIELDS = {
331
  "bootid": ("BootID", constants.QFT_TEXT, "bootid"),
332
  "cnodes": ("CNodes", constants.QFT_NUMBER, "cpu_nodes"),
333
  "csockets": ("CSockets", constants.QFT_NUMBER, "cpu_sockets"),
334
  "ctotal": ("CTotal", constants.QFT_NUMBER, "cpu_total"),
335
  "dfree": ("DFree", constants.QFT_UNIT, "vg_free"),
336
  "dtotal": ("DTotal", constants.QFT_UNIT, "vg_size"),
337
  "mfree": ("MFree", constants.QFT_UNIT, "memory_free"),
338
  "mnode": ("MNode", constants.QFT_UNIT, "memory_dom0"),
339
  "mtotal": ("MTotal", constants.QFT_UNIT, "memory_total"),
340
  }
341

    
342

    
343
def _GetNodeGroup(ctx, node):
344
  """Returns the name of a node's group.
345

346
  @type ctx: L{NodeQueryData}
347
  @type node: L{objects.Node}
348
  @param node: Node object
349

350
  """
351
  ng = ctx.groups.get(node.group, None)
352
  if ng is None:
353
    # Nodes always have a group, or the configuration is corrupt
354
    return (constants.QRFS_UNAVAIL, None)
355

    
356
  return (constants.QRFS_NORMAL, ng.name)
357

    
358

    
359
def _GetLiveNodeField(field, kind, ctx, _):
360
  """Gets the value of a "live" field from L{NodeQueryData}.
361

362
  @param field: Live field name
363
  @param kind: Data kind, one of L{constants.QFT_ALL}
364
  @type ctx: L{NodeQueryData}
365

366
  """
367
  if not ctx.curlive_data:
368
    return (constants.QRFS_NODATA, None)
369

    
370
  try:
371
    value = ctx.curlive_data[field]
372
  except KeyError:
373
    return (constants.QRFS_UNAVAIL, None)
374

    
375
  if kind == constants.QFT_TEXT:
376
    return (constants.QRFS_NORMAL, value)
377

    
378
  assert kind in (constants.QFT_NUMBER, constants.QFT_UNIT)
379

    
380
  # Try to convert into number
381
  try:
382
    return (constants.QRFS_NORMAL, int(value))
383
  except (ValueError, TypeError):
384
    logging.exception("Failed to convert node field '%s' (value %r) to int",
385
                      value, field)
386
    return (constants.QRFS_UNAVAIL, None)
387

    
388

    
389
def _BuildNodeFields():
390
  """Builds list of fields for node queries.
391

392
  """
393
  fields = [
394
    (_MakeField("pip", "PrimaryIP", constants.QFT_TEXT), NQ_CONFIG,
395
     lambda ctx, node: (constants.QRFS_NORMAL, node.primary_ip)),
396
    (_MakeField("sip", "SecondaryIP", constants.QFT_TEXT), NQ_CONFIG,
397
     lambda ctx, node: (constants.QRFS_NORMAL, node.secondary_ip)),
398
    (_MakeField("tags", "Tags", constants.QFT_OTHER), NQ_CONFIG,
399
     lambda ctx, node: (constants.QRFS_NORMAL, list(node.GetTags()))),
400
    (_MakeField("master", "IsMaster", constants.QFT_BOOL), NQ_CONFIG,
401
     lambda ctx, node: (constants.QRFS_NORMAL, node.name == ctx.master_name)),
402
    (_MakeField("role", "Role", constants.QFT_TEXT), NQ_CONFIG,
403
     lambda ctx, node: (constants.QRFS_NORMAL,
404
                        _GetNodeRole(node, ctx.master_name))),
405
    (_MakeField("group", "Group", constants.QFT_TEXT), NQ_GROUP, _GetNodeGroup),
406
    (_MakeField("group.uuid", "GroupUUID", constants.QFT_TEXT),
407
     NQ_CONFIG, lambda ctx, node: (constants.QRFS_NORMAL, node.group)),
408
    ]
409

    
410
  def _GetLength(getter):
411
    return lambda ctx, node: (constants.QRFS_NORMAL,
412
                              len(getter(ctx)[node.name]))
413

    
414
  def _GetList(getter):
415
    return lambda ctx, node: (constants.QRFS_NORMAL,
416
                              list(getter(ctx)[node.name]))
417

    
418
  # Add fields operating on instance lists
419
  for prefix, titleprefix, getter in \
420
      [("p", "Pri", operator.attrgetter("node_to_primary")),
421
       ("s", "Sec", operator.attrgetter("node_to_secondary"))]:
422
    fields.extend([
423
      (_MakeField("%sinst_cnt" % prefix, "%sinst" % prefix.upper(),
424
                  constants.QFT_NUMBER),
425
       NQ_INST, _GetLength(getter)),
426
      (_MakeField("%sinst_list" % prefix, "%sInstances" % titleprefix,
427
                  constants.QFT_OTHER),
428
       NQ_INST, _GetList(getter)),
429
      ])
430

    
431
  # Add simple fields
432
  fields.extend([(_MakeField(name, title, kind), NQ_CONFIG, _GetItemAttr(name))
433
                 for (name, (title, kind)) in _NODE_SIMPLE_FIELDS.items()])
434

    
435
  # Add fields requiring live data
436
  fields.extend([
437
    (_MakeField(name, title, kind), NQ_LIVE,
438
     compat.partial(_GetLiveNodeField, nfield, kind))
439
    for (name, (title, kind, nfield)) in _NODE_LIVE_FIELDS.items()
440
    ])
441

    
442
  return _PrepareFieldList(fields)
443

    
444

    
445
class InstanceQueryData:
446
  """Data container for instance data queries.
447

448
  """
449
  def __init__(self, instances, cluster, disk_usage, offline_nodes, bad_nodes,
450
               live_data):
451
    """Initializes this class.
452

453
    @param instances: List of instance objects
454
    @param cluster: Cluster object
455
    @type disk_usage: dict; instance name as key
456
    @param disk_usage: Per-instance disk usage
457
    @type offline_nodes: list of strings
458
    @param offline_nodes: List of offline nodes
459
    @type bad_nodes: list of strings
460
    @param bad_nodes: List of faulty nodes
461
    @type live_data: dict; instance name as key
462
    @param live_data: Per-instance live data
463

464
    """
465
    assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
466
           "Offline nodes not included in bad nodes"
467
    assert not (set(live_data.keys()) & set(bad_nodes)), \
468
           "Found live data for bad or offline nodes"
469

    
470
    self.instances = instances
471
    self.cluster = cluster
472
    self.disk_usage = disk_usage
473
    self.offline_nodes = offline_nodes
474
    self.bad_nodes = bad_nodes
475
    self.live_data = live_data
476

    
477
    # Used for individual rows
478
    self.inst_hvparams = None
479
    self.inst_beparams = None
480
    self.inst_nicparams = None
481

    
482
  def __iter__(self):
483
    """Iterate over all instances.
484

485
    This function has side-effects and only one instance of the resulting
486
    generator should be used at a time.
487

488
    """
489
    for inst in self.instances:
490
      self.inst_hvparams = self.cluster.FillHV(inst, skip_globals=True)
491
      self.inst_beparams = self.cluster.FillBE(inst)
492
      self.inst_nicparams = [self.cluster.SimpleFillNIC(nic.nicparams)
493
                             for nic in inst.nics]
494

    
495
      yield inst
496

    
497

    
498
def _GetInstOperState(ctx, inst):
499
  """Get instance's operational status.
500

501
  @type ctx: L{InstanceQueryData}
502
  @type inst: L{objects.Instance}
503
  @param inst: Instance object
504

505
  """
506
  if inst.primary_node in ctx.bad_nodes:
507
    return (constants.QRFS_NODATA, None)
508
  else:
509
    return (constants.QRFS_NORMAL, bool(ctx.live_data.get(inst.name)))
510

    
511

    
512
def _GetInstLiveData(name):
513
  """Build function for retrieving live data.
514

515
  @type name: string
516
  @param name: Live data field name
517

518
  """
519
  def fn(ctx, inst):
520
    """Get live data for an instance.
521

522
    @type ctx: L{InstanceQueryData}
523
    @type inst: L{objects.Instance}
524
    @param inst: Instance object
525

526
    """
527
    if (inst.primary_node in ctx.bad_nodes or
528
        inst.primary_node in ctx.offline_nodes):
529
      return (constants.QRFS_NODATA, None)
530

    
531
    if inst.name in ctx.live_data:
532
      data = ctx.live_data[inst.name]
533
      if name in data:
534
        return (constants.QRFS_NORMAL, data[name])
535

    
536
    return (constants.QRFS_UNAVAIL, None)
537

    
538
  return fn
539

    
540

    
541
def _GetInstStatus(ctx, inst):
542
  """Get instance status.
543

544
  @type ctx: L{InstanceQueryData}
545
  @type inst: L{objects.Instance}
546
  @param inst: Instance object
547

548
  """
549
  if inst.primary_node in ctx.offline_nodes:
550
    return (constants.QRFS_NORMAL, "ERROR_nodeoffline")
551

    
552
  if inst.primary_node in ctx.bad_nodes:
553
    return (constants.QRFS_NORMAL, "ERROR_nodedown")
554

    
555
  if bool(ctx.live_data.get(inst.name)):
556
    if inst.admin_up:
557
      return (constants.QRFS_NORMAL, "running")
558
    else:
559
      return (constants.QRFS_NORMAL, "ERROR_up")
560

    
561
  if inst.admin_up:
562
    return (constants.QRFS_NORMAL, "ERROR_down")
563

    
564
  return (constants.QRFS_NORMAL, "ADMIN_down")
565

    
566

    
567
def _GetInstDiskSize(index):
568
  """Build function for retrieving disk size.
569

570
  @type index: int
571
  @param index: Disk index
572

573
  """
574
  def fn(_, inst):
575
    """Get size of a disk.
576

577
    @type inst: L{objects.Instance}
578
    @param inst: Instance object
579

580
    """
581
    try:
582
      return (constants.QRFS_NORMAL, inst.disks[index].size)
583
    except IndexError:
584
      return (constants.QRFS_UNAVAIL, None)
585

    
586
  return fn
587

    
588

    
589
def _GetInstNic(index, cb):
590
  """Build function for calling another function with an instance NIC.
591

592
  @type index: int
593
  @param index: NIC index
594
  @type cb: callable
595
  @param cb: Callback
596

597
  """
598
  def fn(ctx, inst):
599
    """Call helper function with instance NIC.
600

601
    @type ctx: L{InstanceQueryData}
602
    @type inst: L{objects.Instance}
603
    @param inst: Instance object
604

605
    """
606
    try:
607
      nic = inst.nics[index]
608
    except IndexError:
609
      return (constants.QRFS_UNAVAIL, None)
610

    
611
    return cb(ctx, index, nic)
612

    
613
  return fn
614

    
615

    
616
def _GetInstNicIp(ctx, _, nic): # pylint: disable-msg=W0613
617
  """Get a NIC's IP address.
618

619
  @type ctx: L{InstanceQueryData}
620
  @type nic: L{objects.NIC}
621
  @param nic: NIC object
622

623
  """
624
  if nic.ip is None:
625
    return (constants.QRFS_UNAVAIL, None)
626
  else:
627
    return (constants.QRFS_NORMAL, nic.ip)
628

    
629

    
630
def _GetInstNicBridge(ctx, index, _):
631
  """Get a NIC's bridge.
632

633
  @type ctx: L{InstanceQueryData}
634
  @type index: int
635
  @param index: NIC index
636

637
  """
638
  assert len(ctx.inst_nicparams) >= index
639

    
640
  nicparams = ctx.inst_nicparams[index]
641

    
642
  if nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
643
    return (constants.QRFS_NORMAL, nicparams[constants.NIC_LINK])
644
  else:
645
    return (constants.QRFS_UNAVAIL, None)
646

    
647

    
648
def _GetInstAllNicBridges(ctx, inst):
649
  """Get all network bridges for an instance.
650

651
  @type ctx: L{InstanceQueryData}
652
  @type inst: L{objects.Instance}
653
  @param inst: Instance object
654

655
  """
656
  assert len(ctx.inst_nicparams) == len(inst.nics)
657

    
658
  result = []
659

    
660
  for nicp in ctx.inst_nicparams:
661
    if nicp[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
662
      result.append(nicp[constants.NIC_LINK])
663
    else:
664
      result.append(None)
665

    
666
  assert len(result) == len(inst.nics)
667

    
668
  return (constants.QRFS_NORMAL, result)
669

    
670

    
671
def _GetInstNicParam(name):
672
  """Build function for retrieving a NIC parameter.
673

674
  @type name: string
675
  @param name: Parameter name
676

677
  """
678
  def fn(ctx, index, _):
679
    """Get a NIC's bridge.
680

681
    @type ctx: L{InstanceQueryData}
682
    @type inst: L{objects.Instance}
683
    @param inst: Instance object
684
    @type nic: L{objects.NIC}
685
    @param nic: NIC object
686

687
    """
688
    assert len(ctx.inst_nicparams) >= index
689
    return (constants.QRFS_NORMAL, ctx.inst_nicparams[index][name])
690

    
691
  return fn
692

    
693

    
694
def _GetInstanceNetworkFields():
695
  """Get instance fields involving network interfaces.
696

697
  @return: List of field definitions used as input for L{_PrepareFieldList}
698

699
  """
700
  nic_mac_fn = lambda ctx, _, nic: (constants.QRFS_NORMAL, nic.mac)
701
  nic_mode_fn = _GetInstNicParam(constants.NIC_MODE)
702
  nic_link_fn = _GetInstNicParam(constants.NIC_LINK)
703

    
704
  fields = [
705
    # First NIC (legacy)
706
    (_MakeField("ip", "IP_address", constants.QFT_TEXT), IQ_CONFIG,
707
     _GetInstNic(0, _GetInstNicIp)),
708
    (_MakeField("mac", "MAC_address", constants.QFT_TEXT), IQ_CONFIG,
709
     _GetInstNic(0, nic_mac_fn)),
710
    (_MakeField("bridge", "Bridge", constants.QFT_TEXT), IQ_CONFIG,
711
     _GetInstNic(0, _GetInstNicBridge)),
712
    (_MakeField("nic_mode", "NIC_Mode", constants.QFT_TEXT), IQ_CONFIG,
713
     _GetInstNic(0, nic_mode_fn)),
714
    (_MakeField("nic_link", "NIC_Link", constants.QFT_TEXT), IQ_CONFIG,
715
     _GetInstNic(0, nic_link_fn)),
716

    
717
    # All NICs
718
    (_MakeField("nic.count", "NICs", constants.QFT_NUMBER), IQ_CONFIG,
719
     lambda ctx, inst: (constants.QRFS_NORMAL, len(inst.nics))),
720
    (_MakeField("nic.macs", "NIC_MACs", constants.QFT_OTHER), IQ_CONFIG,
721
     lambda ctx, inst: (constants.QRFS_NORMAL, [nic.mac for nic in inst.nics])),
722
    (_MakeField("nic.ips", "NIC_IPs", constants.QFT_OTHER), IQ_CONFIG,
723
     lambda ctx, inst: (constants.QRFS_NORMAL, [nic.ip for nic in inst.nics])),
724
    (_MakeField("nic.modes", "NIC_modes", constants.QFT_OTHER), IQ_CONFIG,
725
     lambda ctx, inst: (constants.QRFS_NORMAL,
726
                        [nicp[constants.NIC_MODE]
727
                         for nicp in ctx.inst_nicparams])),
728
    (_MakeField("nic.links", "NIC_links", constants.QFT_OTHER), IQ_CONFIG,
729
     lambda ctx, inst: (constants.QRFS_NORMAL,
730
                        [nicp[constants.NIC_LINK]
731
                         for nicp in ctx.inst_nicparams])),
732
    (_MakeField("nic.bridges", "NIC_bridges", constants.QFT_OTHER), IQ_CONFIG,
733
     _GetInstAllNicBridges),
734
    ]
735

    
736
  # NICs by number
737
  for i in range(constants.MAX_NICS):
738
    fields.extend([
739
      (_MakeField("nic.ip/%s" % i, "NicIP/%s" % i, constants.QFT_TEXT),
740
       IQ_CONFIG, _GetInstNic(i, _GetInstNicIp)),
741
      (_MakeField("nic.mac/%s" % i, "NicMAC/%s" % i, constants.QFT_TEXT),
742
       IQ_CONFIG, _GetInstNic(i, nic_mac_fn)),
743
      (_MakeField("nic.mode/%s" % i, "NicMode/%s" % i, constants.QFT_TEXT),
744
       IQ_CONFIG, _GetInstNic(i, nic_mode_fn)),
745
      (_MakeField("nic.link/%s" % i, "NicLink/%s" % i, constants.QFT_TEXT),
746
       IQ_CONFIG, _GetInstNic(i, nic_link_fn)),
747
      (_MakeField("nic.bridge/%s" % i, "NicBridge/%s" % i, constants.QFT_TEXT),
748
       IQ_CONFIG, _GetInstNic(i, _GetInstNicBridge)),
749
      ])
750

    
751
  return fields
752

    
753

    
754
def _GetInstDiskUsage(ctx, inst):
755
  """Get disk usage for an instance.
756

757
  @type ctx: L{InstanceQueryData}
758
  @type inst: L{objects.Instance}
759
  @param inst: Instance object
760

761
  """
762
  usage = ctx.disk_usage[inst.name]
763

    
764
  if usage is None:
765
    usage = 0
766

    
767
  return (constants.QRFS_NORMAL, usage)
768

    
769

    
770
def _GetInstanceDiskFields():
771
  """Get instance fields involving disks.
772

773
  @return: List of field definitions used as input for L{_PrepareFieldList}
774

775
  """
776
  fields = [
777
    (_MakeField("disk_usage", "DiskUsage", constants.QFT_UNIT), IQ_DISKUSAGE,
778
     _GetInstDiskUsage),
779
    (_MakeField("sda_size", "LegacyDisk/0", constants.QFT_UNIT), IQ_CONFIG,
780
     _GetInstDiskSize(0)),
781
    (_MakeField("sdb_size", "LegacyDisk/1", constants.QFT_UNIT), IQ_CONFIG,
782
     _GetInstDiskSize(1)),
783
    (_MakeField("disk.count", "Disks", constants.QFT_NUMBER), IQ_CONFIG,
784
     lambda ctx, inst: (constants.QRFS_NORMAL, len(inst.disks))),
785
    (_MakeField("disk.sizes", "Disk_sizes", constants.QFT_OTHER), IQ_CONFIG,
786
     lambda ctx, inst: (constants.QRFS_NORMAL,
787
                        [disk.size for disk in inst.disks])),
788
    ]
789

    
790
  # Disks by number
791
  fields.extend([
792
    (_MakeField("disk.size/%s" % i, "Disk/%s" % i, constants.QFT_UNIT),
793
     IQ_CONFIG, _GetInstDiskSize(i))
794
    for i in range(constants.MAX_DISKS)
795
    ])
796

    
797
  return fields
798

    
799

    
800
def _GetInstanceParameterFields():
801
  """Get instance fields involving parameters.
802

803
  @return: List of field definitions used as input for L{_PrepareFieldList}
804

805
  """
806
  # TODO: Consider moving titles closer to constants
807
  be_title = {
808
    constants.BE_AUTO_BALANCE: "Auto_balance",
809
    constants.BE_MEMORY: "Configured_memory",
810
    constants.BE_VCPUS: "VCPUs",
811
    }
812

    
813
  hv_title = {
814
    constants.HV_ACPI: "ACPI",
815
    constants.HV_BOOT_ORDER: "Boot_order",
816
    constants.HV_CDROM_IMAGE_PATH: "CDROM_image_path",
817
    constants.HV_DISK_TYPE: "Disk_type",
818
    constants.HV_INITRD_PATH: "Initrd_path",
819
    constants.HV_KERNEL_PATH: "Kernel_path",
820
    constants.HV_NIC_TYPE: "NIC_type",
821
    constants.HV_PAE: "PAE",
822
    constants.HV_VNC_BIND_ADDRESS: "VNC_bind_address",
823
    }
824

    
825
  fields = [
826
    # Filled parameters
827
    (_MakeField("hvparams", "HypervisorParameters", constants.QFT_OTHER),
828
     IQ_CONFIG, lambda ctx, _: (constants.QRFS_NORMAL, ctx.inst_hvparams)),
829
    (_MakeField("beparams", "BackendParameters", constants.QFT_OTHER),
830
     IQ_CONFIG, lambda ctx, _: (constants.QRFS_NORMAL, ctx.inst_beparams)),
831
    (_MakeField("vcpus", "LegacyVCPUs", constants.QFT_NUMBER), IQ_CONFIG,
832
     lambda ctx, _: (constants.QRFS_NORMAL,
833
                     ctx.inst_beparams[constants.BE_VCPUS])),
834

    
835
    # Unfilled parameters
836
    (_MakeField("custom_hvparams", "CustomHypervisorParameters",
837
                constants.QFT_OTHER),
838
     IQ_CONFIG, lambda ctx, inst: (constants.QRFS_NORMAL, inst.hvparams)),
839
    (_MakeField("custom_beparams", "CustomBackendParameters",
840
                constants.QFT_OTHER),
841
     IQ_CONFIG, lambda ctx, inst: (constants.QRFS_NORMAL, inst.beparams)),
842
    (_MakeField("custom_nicparams", "CustomNicParameters",
843
                constants.QFT_OTHER),
844
     IQ_CONFIG, lambda ctx, inst: (constants.QRFS_NORMAL,
845
                                   [nic.nicparams for nic in inst.nics])),
846
    ]
847

    
848
  # HV params
849
  def _GetInstHvParam(name):
850
    return lambda ctx, _: (constants.QRFS_NORMAL,
851
                           ctx.inst_hvparams.get(name, None))
852

    
853
  fields.extend([
854
    # For now all hypervisor parameters are exported as QFT_OTHER
855
    (_MakeField("hv/%s" % name, hv_title.get(name, "hv/%s" % name),
856
                constants.QFT_OTHER),
857
     IQ_CONFIG, _GetInstHvParam(name))
858
    for name in constants.HVS_PARAMETERS
859
    if name not in constants.HVC_GLOBALS
860
    ])
861

    
862
  # BE params
863
  def _GetInstBeParam(name):
864
    return lambda ctx, _: (constants.QRFS_NORMAL,
865
                           ctx.inst_beparams.get(name, None))
866

    
867
  fields.extend([
868
    # For now all backend parameters are exported as QFT_OTHER
869
    (_MakeField("be/%s" % name, be_title.get(name, "be/%s" % name),
870
                constants.QFT_OTHER),
871
     IQ_CONFIG, _GetInstBeParam(name))
872
    for name in constants.BES_PARAMETERS
873
    ])
874

    
875
  return fields
876

    
877

    
878
_INST_SIMPLE_FIELDS = {
879
  "ctime": ("CTime", constants.QFT_TIMESTAMP),
880
  "disk_template": ("Disk_template", constants.QFT_TEXT),
881
  "hypervisor": ("Hypervisor", constants.QFT_TEXT),
882
  "mtime": ("MTime", constants.QFT_TIMESTAMP),
883
  "name": ("Node", constants.QFT_TEXT),
884
  # Depending on the hypervisor, the port can be None
885
  "network_port": ("Network_port", constants.QFT_OTHER),
886
  "os": ("OS", constants.QFT_TEXT),
887
  "serial_no": ("SerialNo", constants.QFT_NUMBER),
888
  "uuid": ("UUID", constants.QFT_TEXT),
889
  }
890

    
891

    
892
def _BuildInstanceFields():
893
  """Builds list of fields for instance queries.
894

895
  """
896
  fields = [
897
    (_MakeField("pnode", "Primary_node", constants.QFT_TEXT), IQ_CONFIG,
898
     lambda ctx, inst: (constants.QRFS_NORMAL, inst.primary_node)),
899
    (_MakeField("snodes", "Secondary_Nodes", constants.QFT_OTHER), IQ_CONFIG,
900
     lambda ctx, inst: (constants.QRFS_NORMAL, list(inst.secondary_nodes))),
901
    (_MakeField("admin_state", "Autostart", constants.QFT_BOOL), IQ_CONFIG,
902
     lambda ctx, inst: (constants.QRFS_NORMAL, inst.admin_up)),
903
    (_MakeField("tags", "Tags", constants.QFT_OTHER), IQ_CONFIG,
904
     lambda ctx, inst: (constants.QRFS_NORMAL, list(inst.GetTags()))),
905
    ]
906

    
907
  # Add simple fields
908
  fields.extend([(_MakeField(name, title, kind), IQ_CONFIG, _GetItemAttr(name))
909
                 for (name, (title, kind)) in _INST_SIMPLE_FIELDS.items()])
910

    
911
  # Fields requiring talking to the node
912
  fields.extend([
913
    (_MakeField("oper_state", "Running", constants.QFT_BOOL), IQ_LIVE,
914
     _GetInstOperState),
915
    (_MakeField("oper_ram", "RuntimeMemory", constants.QFT_UNIT), IQ_LIVE,
916
     _GetInstLiveData("memory")),
917
    (_MakeField("oper_vcpus", "RuntimeVCPUs", constants.QFT_NUMBER), IQ_LIVE,
918
     _GetInstLiveData("vcpus")),
919
    (_MakeField("status", "Status", constants.QFT_TEXT), IQ_LIVE,
920
     _GetInstStatus),
921
    ])
922

    
923
  fields.extend(_GetInstanceParameterFields())
924
  fields.extend(_GetInstanceDiskFields())
925
  fields.extend(_GetInstanceNetworkFields())
926

    
927
  return _PrepareFieldList(fields)
928

    
929

    
930
#: Fields available for node queries
931
NODE_FIELDS = _BuildNodeFields()
932

    
933
#: Fields available for instance queries
934
INSTANCE_FIELDS = _BuildInstanceFields()