Statistics
| Branch: | Tag: | Revision:

root / lib / query.py @ aa29e95f

History | View | Annotate | Download (28.6 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 QueryFields(fielddefs, selected):
239
  """Returns list of available fields.
240

241
  @type fielddefs: dict
242
  @param fielddefs: Field definitions
243
  @type selected: list of strings
244
  @param selected: List of selected fields
245
  @return: List of L{objects.QueryFieldDefinition}
246

247
  """
248
  if selected is None:
249
    # Client requests all fields, sort by name
250
    fdefs = utils.NiceSort(GetAllFields(fielddefs.values()),
251
                           key=operator.attrgetter("name"))
252
  else:
253
    # Keep order as requested by client
254
    fdefs = Query(fielddefs, selected).GetFields()
255

    
256
  return objects.QueryFieldsResponse(fields=fdefs).ToDict()
257

    
258

    
259
def _MakeField(name, title, kind):
260
  """Wrapper for creating L{objects.QueryFieldDefinition} instances.
261

262
  @param name: Field name as a regular expression
263
  @param title: Human-readable title
264
  @param kind: Field type
265

266
  """
267
  return objects.QueryFieldDefinition(name=name, title=title, kind=kind)
268

    
269

    
270
def _GetNodeRole(node, master_name):
271
  """Determine node role.
272

273
  @type node: L{objects.Node}
274
  @param node: Node object
275
  @type master_name: string
276
  @param master_name: Master node name
277

278
  """
279
  if node.name == master_name:
280
    return "M"
281
  elif node.master_candidate:
282
    return "C"
283
  elif node.drained:
284
    return "D"
285
  elif node.offline:
286
    return "O"
287
  else:
288
    return "R"
289

    
290

    
291
def _GetItemAttr(attr):
292
  """Returns a field function to return an attribute of the item.
293

294
  @param attr: Attribute name
295

296
  """
297
  getter = operator.attrgetter(attr)
298
  return lambda _, item: (constants.QRFS_NORMAL, getter(item))
299

    
300

    
301
def _GetItemTimestamp(getter):
302
  """Returns function for getting timestamp of item.
303

304
  @type getter: callable
305
  @param getter: Function to retrieve timestamp attribute
306

307
  """
308
  def fn(_, item):
309
    """Returns a timestamp of item.
310

311
    """
312
    timestamp = getter(item)
313
    if timestamp is None:
314
      # Old configs might not have all timestamps
315
      return (constants.QRFS_UNAVAIL, None)
316
    else:
317
      return (constants.QRFS_NORMAL, timestamp)
318

    
319
  return fn
320

    
321

    
322
def _GetItemTimestampFields(datatype):
323
  """Returns common timestamp fields.
324

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

327
  """
328
  return [
329
    (_MakeField("ctime", "CTime", constants.QFT_TIMESTAMP), datatype,
330
     _GetItemTimestamp(operator.attrgetter("ctime"))),
331
    (_MakeField("mtime", "MTime", constants.QFT_TIMESTAMP), datatype,
332
     _GetItemTimestamp(operator.attrgetter("mtime"))),
333
    ]
334

    
335

    
336
class NodeQueryData:
337
  """Data container for node data queries.
338

339
  """
340
  def __init__(self, nodes, live_data, master_name, node_to_primary,
341
               node_to_secondary, groups):
342
    """Initializes this class.
343

344
    """
345
    self.nodes = nodes
346
    self.live_data = live_data
347
    self.master_name = master_name
348
    self.node_to_primary = node_to_primary
349
    self.node_to_secondary = node_to_secondary
350
    self.groups = groups
351

    
352
    # Used for individual rows
353
    self.curlive_data = None
354

    
355
  def __iter__(self):
356
    """Iterate over all nodes.
357

358
    This function has side-effects and only one instance of the resulting
359
    generator should be used at a time.
360

361
    """
362
    for node in self.nodes:
363
      if self.live_data:
364
        self.curlive_data = self.live_data.get(node.name, None)
365
      else:
366
        self.curlive_data = None
367
      yield node
368

    
369

    
370
#: Fields that are direct attributes of an L{objects.Node} object
371
_NODE_SIMPLE_FIELDS = {
372
  "drained": ("Drained", constants.QFT_BOOL),
373
  "master_candidate": ("MasterC", constants.QFT_BOOL),
374
  "master_capable": ("MasterCapable", constants.QFT_BOOL),
375
  "name": ("Node", constants.QFT_TEXT),
376
  "offline": ("Offline", constants.QFT_BOOL),
377
  "serial_no": ("SerialNo", constants.QFT_NUMBER),
378
  "uuid": ("UUID", constants.QFT_TEXT),
379
  "vm_capable": ("VMCapable", constants.QFT_BOOL),
380
  }
381

    
382

    
383
#: Fields requiring talking to the node
384
_NODE_LIVE_FIELDS = {
385
  "bootid": ("BootID", constants.QFT_TEXT, "bootid"),
386
  "cnodes": ("CNodes", constants.QFT_NUMBER, "cpu_nodes"),
387
  "csockets": ("CSockets", constants.QFT_NUMBER, "cpu_sockets"),
388
  "ctotal": ("CTotal", constants.QFT_NUMBER, "cpu_total"),
389
  "dfree": ("DFree", constants.QFT_UNIT, "vg_free"),
390
  "dtotal": ("DTotal", constants.QFT_UNIT, "vg_size"),
391
  "mfree": ("MFree", constants.QFT_UNIT, "memory_free"),
392
  "mnode": ("MNode", constants.QFT_UNIT, "memory_dom0"),
393
  "mtotal": ("MTotal", constants.QFT_UNIT, "memory_total"),
394
  }
395

    
396

    
397
def _GetNodeGroup(ctx, node):
398
  """Returns the name of a node's group.
399

400
  @type ctx: L{NodeQueryData}
401
  @type node: L{objects.Node}
402
  @param node: Node object
403

404
  """
405
  ng = ctx.groups.get(node.group, None)
406
  if ng is None:
407
    # Nodes always have a group, or the configuration is corrupt
408
    return (constants.QRFS_UNAVAIL, None)
409

    
410
  return (constants.QRFS_NORMAL, ng.name)
411

    
412

    
413
def _GetLiveNodeField(field, kind, ctx, _):
414
  """Gets the value of a "live" field from L{NodeQueryData}.
415

416
  @param field: Live field name
417
  @param kind: Data kind, one of L{constants.QFT_ALL}
418
  @type ctx: L{NodeQueryData}
419

420
  """
421
  if not ctx.curlive_data:
422
    return (constants.QRFS_NODATA, None)
423

    
424
  try:
425
    value = ctx.curlive_data[field]
426
  except KeyError:
427
    return (constants.QRFS_UNAVAIL, None)
428

    
429
  if kind == constants.QFT_TEXT:
430
    return (constants.QRFS_NORMAL, value)
431

    
432
  assert kind in (constants.QFT_NUMBER, constants.QFT_UNIT)
433

    
434
  # Try to convert into number
435
  try:
436
    return (constants.QRFS_NORMAL, int(value))
437
  except (ValueError, TypeError):
438
    logging.exception("Failed to convert node field '%s' (value %r) to int",
439
                      value, field)
440
    return (constants.QRFS_UNAVAIL, None)
441

    
442

    
443
def _BuildNodeFields():
444
  """Builds list of fields for node queries.
445

446
  """
447
  fields = [
448
    (_MakeField("pip", "PrimaryIP", constants.QFT_TEXT), NQ_CONFIG,
449
     lambda ctx, node: (constants.QRFS_NORMAL, node.primary_ip)),
450
    (_MakeField("sip", "SecondaryIP", constants.QFT_TEXT), NQ_CONFIG,
451
     lambda ctx, node: (constants.QRFS_NORMAL, node.secondary_ip)),
452
    (_MakeField("tags", "Tags", constants.QFT_OTHER), NQ_CONFIG,
453
     lambda ctx, node: (constants.QRFS_NORMAL, list(node.GetTags()))),
454
    (_MakeField("master", "IsMaster", constants.QFT_BOOL), NQ_CONFIG,
455
     lambda ctx, node: (constants.QRFS_NORMAL, node.name == ctx.master_name)),
456
    (_MakeField("role", "Role", constants.QFT_TEXT), NQ_CONFIG,
457
     lambda ctx, node: (constants.QRFS_NORMAL,
458
                        _GetNodeRole(node, ctx.master_name))),
459
    (_MakeField("group", "Group", constants.QFT_TEXT), NQ_GROUP, _GetNodeGroup),
460
    (_MakeField("group.uuid", "GroupUUID", constants.QFT_TEXT),
461
     NQ_CONFIG, lambda ctx, node: (constants.QRFS_NORMAL, node.group)),
462
    ]
463

    
464
  def _GetLength(getter):
465
    return lambda ctx, node: (constants.QRFS_NORMAL,
466
                              len(getter(ctx)[node.name]))
467

    
468
  def _GetList(getter):
469
    return lambda ctx, node: (constants.QRFS_NORMAL,
470
                              list(getter(ctx)[node.name]))
471

    
472
  # Add fields operating on instance lists
473
  for prefix, titleprefix, getter in \
474
      [("p", "Pri", operator.attrgetter("node_to_primary")),
475
       ("s", "Sec", operator.attrgetter("node_to_secondary"))]:
476
    fields.extend([
477
      (_MakeField("%sinst_cnt" % prefix, "%sinst" % prefix.upper(),
478
                  constants.QFT_NUMBER),
479
       NQ_INST, _GetLength(getter)),
480
      (_MakeField("%sinst_list" % prefix, "%sInstances" % titleprefix,
481
                  constants.QFT_OTHER),
482
       NQ_INST, _GetList(getter)),
483
      ])
484

    
485
  # Add simple fields
486
  fields.extend([(_MakeField(name, title, kind), NQ_CONFIG, _GetItemAttr(name))
487
                 for (name, (title, kind)) in _NODE_SIMPLE_FIELDS.items()])
488

    
489
  # Add fields requiring live data
490
  fields.extend([
491
    (_MakeField(name, title, kind), NQ_LIVE,
492
     compat.partial(_GetLiveNodeField, nfield, kind))
493
    for (name, (title, kind, nfield)) in _NODE_LIVE_FIELDS.items()
494
    ])
495

    
496
  # Add timestamps
497
  fields.extend(_GetItemTimestampFields(NQ_CONFIG))
498

    
499
  return _PrepareFieldList(fields)
500

    
501

    
502
class InstanceQueryData:
503
  """Data container for instance data queries.
504

505
  """
506
  def __init__(self, instances, cluster, disk_usage, offline_nodes, bad_nodes,
507
               live_data):
508
    """Initializes this class.
509

510
    @param instances: List of instance objects
511
    @param cluster: Cluster object
512
    @type disk_usage: dict; instance name as key
513
    @param disk_usage: Per-instance disk usage
514
    @type offline_nodes: list of strings
515
    @param offline_nodes: List of offline nodes
516
    @type bad_nodes: list of strings
517
    @param bad_nodes: List of faulty nodes
518
    @type live_data: dict; instance name as key
519
    @param live_data: Per-instance live data
520

521
    """
522
    assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
523
           "Offline nodes not included in bad nodes"
524
    assert not (set(live_data.keys()) & set(bad_nodes)), \
525
           "Found live data for bad or offline nodes"
526

    
527
    self.instances = instances
528
    self.cluster = cluster
529
    self.disk_usage = disk_usage
530
    self.offline_nodes = offline_nodes
531
    self.bad_nodes = bad_nodes
532
    self.live_data = live_data
533

    
534
    # Used for individual rows
535
    self.inst_hvparams = None
536
    self.inst_beparams = None
537
    self.inst_nicparams = None
538

    
539
  def __iter__(self):
540
    """Iterate over all instances.
541

542
    This function has side-effects and only one instance of the resulting
543
    generator should be used at a time.
544

545
    """
546
    for inst in self.instances:
547
      self.inst_hvparams = self.cluster.FillHV(inst, skip_globals=True)
548
      self.inst_beparams = self.cluster.FillBE(inst)
549
      self.inst_nicparams = [self.cluster.SimpleFillNIC(nic.nicparams)
550
                             for nic in inst.nics]
551

    
552
      yield inst
553

    
554

    
555
def _GetInstOperState(ctx, inst):
556
  """Get instance's operational status.
557

558
  @type ctx: L{InstanceQueryData}
559
  @type inst: L{objects.Instance}
560
  @param inst: Instance object
561

562
  """
563
  if inst.primary_node in ctx.bad_nodes:
564
    return (constants.QRFS_NODATA, None)
565
  else:
566
    return (constants.QRFS_NORMAL, bool(ctx.live_data.get(inst.name)))
567

    
568

    
569
def _GetInstLiveData(name):
570
  """Build function for retrieving live data.
571

572
  @type name: string
573
  @param name: Live data field name
574

575
  """
576
  def fn(ctx, inst):
577
    """Get live data for an instance.
578

579
    @type ctx: L{InstanceQueryData}
580
    @type inst: L{objects.Instance}
581
    @param inst: Instance object
582

583
    """
584
    if (inst.primary_node in ctx.bad_nodes or
585
        inst.primary_node in ctx.offline_nodes):
586
      return (constants.QRFS_NODATA, None)
587

    
588
    if inst.name in ctx.live_data:
589
      data = ctx.live_data[inst.name]
590
      if name in data:
591
        return (constants.QRFS_NORMAL, data[name])
592

    
593
    return (constants.QRFS_UNAVAIL, None)
594

    
595
  return fn
596

    
597

    
598
def _GetInstStatus(ctx, inst):
599
  """Get instance status.
600

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

605
  """
606
  if inst.primary_node in ctx.offline_nodes:
607
    return (constants.QRFS_NORMAL, "ERROR_nodeoffline")
608

    
609
  if inst.primary_node in ctx.bad_nodes:
610
    return (constants.QRFS_NORMAL, "ERROR_nodedown")
611

    
612
  if bool(ctx.live_data.get(inst.name)):
613
    if inst.admin_up:
614
      return (constants.QRFS_NORMAL, "running")
615
    else:
616
      return (constants.QRFS_NORMAL, "ERROR_up")
617

    
618
  if inst.admin_up:
619
    return (constants.QRFS_NORMAL, "ERROR_down")
620

    
621
  return (constants.QRFS_NORMAL, "ADMIN_down")
622

    
623

    
624
def _GetInstDiskSize(index):
625
  """Build function for retrieving disk size.
626

627
  @type index: int
628
  @param index: Disk index
629

630
  """
631
  def fn(_, inst):
632
    """Get size of a disk.
633

634
    @type inst: L{objects.Instance}
635
    @param inst: Instance object
636

637
    """
638
    try:
639
      return (constants.QRFS_NORMAL, inst.disks[index].size)
640
    except IndexError:
641
      return (constants.QRFS_UNAVAIL, None)
642

    
643
  return fn
644

    
645

    
646
def _GetInstNic(index, cb):
647
  """Build function for calling another function with an instance NIC.
648

649
  @type index: int
650
  @param index: NIC index
651
  @type cb: callable
652
  @param cb: Callback
653

654
  """
655
  def fn(ctx, inst):
656
    """Call helper function with instance NIC.
657

658
    @type ctx: L{InstanceQueryData}
659
    @type inst: L{objects.Instance}
660
    @param inst: Instance object
661

662
    """
663
    try:
664
      nic = inst.nics[index]
665
    except IndexError:
666
      return (constants.QRFS_UNAVAIL, None)
667

    
668
    return cb(ctx, index, nic)
669

    
670
  return fn
671

    
672

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

676
  @type ctx: L{InstanceQueryData}
677
  @type nic: L{objects.NIC}
678
  @param nic: NIC object
679

680
  """
681
  if nic.ip is None:
682
    return (constants.QRFS_UNAVAIL, None)
683
  else:
684
    return (constants.QRFS_NORMAL, nic.ip)
685

    
686

    
687
def _GetInstNicBridge(ctx, index, _):
688
  """Get a NIC's bridge.
689

690
  @type ctx: L{InstanceQueryData}
691
  @type index: int
692
  @param index: NIC index
693

694
  """
695
  assert len(ctx.inst_nicparams) >= index
696

    
697
  nicparams = ctx.inst_nicparams[index]
698

    
699
  if nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
700
    return (constants.QRFS_NORMAL, nicparams[constants.NIC_LINK])
701
  else:
702
    return (constants.QRFS_UNAVAIL, None)
703

    
704

    
705
def _GetInstAllNicBridges(ctx, inst):
706
  """Get all network bridges for an instance.
707

708
  @type ctx: L{InstanceQueryData}
709
  @type inst: L{objects.Instance}
710
  @param inst: Instance object
711

712
  """
713
  assert len(ctx.inst_nicparams) == len(inst.nics)
714

    
715
  result = []
716

    
717
  for nicp in ctx.inst_nicparams:
718
    if nicp[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
719
      result.append(nicp[constants.NIC_LINK])
720
    else:
721
      result.append(None)
722

    
723
  assert len(result) == len(inst.nics)
724

    
725
  return (constants.QRFS_NORMAL, result)
726

    
727

    
728
def _GetInstNicParam(name):
729
  """Build function for retrieving a NIC parameter.
730

731
  @type name: string
732
  @param name: Parameter name
733

734
  """
735
  def fn(ctx, index, _):
736
    """Get a NIC's bridge.
737

738
    @type ctx: L{InstanceQueryData}
739
    @type inst: L{objects.Instance}
740
    @param inst: Instance object
741
    @type nic: L{objects.NIC}
742
    @param nic: NIC object
743

744
    """
745
    assert len(ctx.inst_nicparams) >= index
746
    return (constants.QRFS_NORMAL, ctx.inst_nicparams[index][name])
747

    
748
  return fn
749

    
750

    
751
def _GetInstanceNetworkFields():
752
  """Get instance fields involving network interfaces.
753

754
  @return: List of field definitions used as input for L{_PrepareFieldList}
755

756
  """
757
  nic_mac_fn = lambda ctx, _, nic: (constants.QRFS_NORMAL, nic.mac)
758
  nic_mode_fn = _GetInstNicParam(constants.NIC_MODE)
759
  nic_link_fn = _GetInstNicParam(constants.NIC_LINK)
760

    
761
  fields = [
762
    # First NIC (legacy)
763
    (_MakeField("ip", "IP_address", constants.QFT_TEXT), IQ_CONFIG,
764
     _GetInstNic(0, _GetInstNicIp)),
765
    (_MakeField("mac", "MAC_address", constants.QFT_TEXT), IQ_CONFIG,
766
     _GetInstNic(0, nic_mac_fn)),
767
    (_MakeField("bridge", "Bridge", constants.QFT_TEXT), IQ_CONFIG,
768
     _GetInstNic(0, _GetInstNicBridge)),
769
    (_MakeField("nic_mode", "NIC_Mode", constants.QFT_TEXT), IQ_CONFIG,
770
     _GetInstNic(0, nic_mode_fn)),
771
    (_MakeField("nic_link", "NIC_Link", constants.QFT_TEXT), IQ_CONFIG,
772
     _GetInstNic(0, nic_link_fn)),
773

    
774
    # All NICs
775
    (_MakeField("nic.count", "NICs", constants.QFT_NUMBER), IQ_CONFIG,
776
     lambda ctx, inst: (constants.QRFS_NORMAL, len(inst.nics))),
777
    (_MakeField("nic.macs", "NIC_MACs", constants.QFT_OTHER), IQ_CONFIG,
778
     lambda ctx, inst: (constants.QRFS_NORMAL, [nic.mac for nic in inst.nics])),
779
    (_MakeField("nic.ips", "NIC_IPs", constants.QFT_OTHER), IQ_CONFIG,
780
     lambda ctx, inst: (constants.QRFS_NORMAL, [nic.ip for nic in inst.nics])),
781
    (_MakeField("nic.modes", "NIC_modes", constants.QFT_OTHER), IQ_CONFIG,
782
     lambda ctx, inst: (constants.QRFS_NORMAL,
783
                        [nicp[constants.NIC_MODE]
784
                         for nicp in ctx.inst_nicparams])),
785
    (_MakeField("nic.links", "NIC_links", constants.QFT_OTHER), IQ_CONFIG,
786
     lambda ctx, inst: (constants.QRFS_NORMAL,
787
                        [nicp[constants.NIC_LINK]
788
                         for nicp in ctx.inst_nicparams])),
789
    (_MakeField("nic.bridges", "NIC_bridges", constants.QFT_OTHER), IQ_CONFIG,
790
     _GetInstAllNicBridges),
791
    ]
792

    
793
  # NICs by number
794
  for i in range(constants.MAX_NICS):
795
    fields.extend([
796
      (_MakeField("nic.ip/%s" % i, "NicIP/%s" % i, constants.QFT_TEXT),
797
       IQ_CONFIG, _GetInstNic(i, _GetInstNicIp)),
798
      (_MakeField("nic.mac/%s" % i, "NicMAC/%s" % i, constants.QFT_TEXT),
799
       IQ_CONFIG, _GetInstNic(i, nic_mac_fn)),
800
      (_MakeField("nic.mode/%s" % i, "NicMode/%s" % i, constants.QFT_TEXT),
801
       IQ_CONFIG, _GetInstNic(i, nic_mode_fn)),
802
      (_MakeField("nic.link/%s" % i, "NicLink/%s" % i, constants.QFT_TEXT),
803
       IQ_CONFIG, _GetInstNic(i, nic_link_fn)),
804
      (_MakeField("nic.bridge/%s" % i, "NicBridge/%s" % i, constants.QFT_TEXT),
805
       IQ_CONFIG, _GetInstNic(i, _GetInstNicBridge)),
806
      ])
807

    
808
  return fields
809

    
810

    
811
def _GetInstDiskUsage(ctx, inst):
812
  """Get disk usage for an instance.
813

814
  @type ctx: L{InstanceQueryData}
815
  @type inst: L{objects.Instance}
816
  @param inst: Instance object
817

818
  """
819
  usage = ctx.disk_usage[inst.name]
820

    
821
  if usage is None:
822
    usage = 0
823

    
824
  return (constants.QRFS_NORMAL, usage)
825

    
826

    
827
def _GetInstanceDiskFields():
828
  """Get instance fields involving disks.
829

830
  @return: List of field definitions used as input for L{_PrepareFieldList}
831

832
  """
833
  fields = [
834
    (_MakeField("disk_usage", "DiskUsage", constants.QFT_UNIT), IQ_DISKUSAGE,
835
     _GetInstDiskUsage),
836
    (_MakeField("sda_size", "LegacyDisk/0", constants.QFT_UNIT), IQ_CONFIG,
837
     _GetInstDiskSize(0)),
838
    (_MakeField("sdb_size", "LegacyDisk/1", constants.QFT_UNIT), IQ_CONFIG,
839
     _GetInstDiskSize(1)),
840
    (_MakeField("disk.count", "Disks", constants.QFT_NUMBER), IQ_CONFIG,
841
     lambda ctx, inst: (constants.QRFS_NORMAL, len(inst.disks))),
842
    (_MakeField("disk.sizes", "Disk_sizes", constants.QFT_OTHER), IQ_CONFIG,
843
     lambda ctx, inst: (constants.QRFS_NORMAL,
844
                        [disk.size for disk in inst.disks])),
845
    ]
846

    
847
  # Disks by number
848
  fields.extend([
849
    (_MakeField("disk.size/%s" % i, "Disk/%s" % i, constants.QFT_UNIT),
850
     IQ_CONFIG, _GetInstDiskSize(i))
851
    for i in range(constants.MAX_DISKS)
852
    ])
853

    
854
  return fields
855

    
856

    
857
def _GetInstanceParameterFields():
858
  """Get instance fields involving parameters.
859

860
  @return: List of field definitions used as input for L{_PrepareFieldList}
861

862
  """
863
  # TODO: Consider moving titles closer to constants
864
  be_title = {
865
    constants.BE_AUTO_BALANCE: "Auto_balance",
866
    constants.BE_MEMORY: "Configured_memory",
867
    constants.BE_VCPUS: "VCPUs",
868
    }
869

    
870
  hv_title = {
871
    constants.HV_ACPI: "ACPI",
872
    constants.HV_BOOT_ORDER: "Boot_order",
873
    constants.HV_CDROM_IMAGE_PATH: "CDROM_image_path",
874
    constants.HV_DISK_TYPE: "Disk_type",
875
    constants.HV_INITRD_PATH: "Initrd_path",
876
    constants.HV_KERNEL_PATH: "Kernel_path",
877
    constants.HV_NIC_TYPE: "NIC_type",
878
    constants.HV_PAE: "PAE",
879
    constants.HV_VNC_BIND_ADDRESS: "VNC_bind_address",
880
    }
881

    
882
  fields = [
883
    # Filled parameters
884
    (_MakeField("hvparams", "HypervisorParameters", constants.QFT_OTHER),
885
     IQ_CONFIG, lambda ctx, _: (constants.QRFS_NORMAL, ctx.inst_hvparams)),
886
    (_MakeField("beparams", "BackendParameters", constants.QFT_OTHER),
887
     IQ_CONFIG, lambda ctx, _: (constants.QRFS_NORMAL, ctx.inst_beparams)),
888
    (_MakeField("vcpus", "LegacyVCPUs", constants.QFT_NUMBER), IQ_CONFIG,
889
     lambda ctx, _: (constants.QRFS_NORMAL,
890
                     ctx.inst_beparams[constants.BE_VCPUS])),
891

    
892
    # Unfilled parameters
893
    (_MakeField("custom_hvparams", "CustomHypervisorParameters",
894
                constants.QFT_OTHER),
895
     IQ_CONFIG, lambda ctx, inst: (constants.QRFS_NORMAL, inst.hvparams)),
896
    (_MakeField("custom_beparams", "CustomBackendParameters",
897
                constants.QFT_OTHER),
898
     IQ_CONFIG, lambda ctx, inst: (constants.QRFS_NORMAL, inst.beparams)),
899
    (_MakeField("custom_nicparams", "CustomNicParameters",
900
                constants.QFT_OTHER),
901
     IQ_CONFIG, lambda ctx, inst: (constants.QRFS_NORMAL,
902
                                   [nic.nicparams for nic in inst.nics])),
903
    ]
904

    
905
  # HV params
906
  def _GetInstHvParam(name):
907
    return lambda ctx, _: (constants.QRFS_NORMAL,
908
                           ctx.inst_hvparams.get(name, None))
909

    
910
  fields.extend([
911
    # For now all hypervisor parameters are exported as QFT_OTHER
912
    (_MakeField("hv/%s" % name, hv_title.get(name, "hv/%s" % name),
913
                constants.QFT_OTHER),
914
     IQ_CONFIG, _GetInstHvParam(name))
915
    for name in constants.HVS_PARAMETERS
916
    if name not in constants.HVC_GLOBALS
917
    ])
918

    
919
  # BE params
920
  def _GetInstBeParam(name):
921
    return lambda ctx, _: (constants.QRFS_NORMAL,
922
                           ctx.inst_beparams.get(name, None))
923

    
924
  fields.extend([
925
    # For now all backend parameters are exported as QFT_OTHER
926
    (_MakeField("be/%s" % name, be_title.get(name, "be/%s" % name),
927
                constants.QFT_OTHER),
928
     IQ_CONFIG, _GetInstBeParam(name))
929
    for name in constants.BES_PARAMETERS
930
    ])
931

    
932
  return fields
933

    
934

    
935
_INST_SIMPLE_FIELDS = {
936
  "disk_template": ("Disk_template", constants.QFT_TEXT),
937
  "hypervisor": ("Hypervisor", constants.QFT_TEXT),
938
  "name": ("Node", constants.QFT_TEXT),
939
  # Depending on the hypervisor, the port can be None
940
  "network_port": ("Network_port", constants.QFT_OTHER),
941
  "os": ("OS", constants.QFT_TEXT),
942
  "serial_no": ("SerialNo", constants.QFT_NUMBER),
943
  "uuid": ("UUID", constants.QFT_TEXT),
944
  }
945

    
946

    
947
def _BuildInstanceFields():
948
  """Builds list of fields for instance queries.
949

950
  """
951
  fields = [
952
    (_MakeField("pnode", "Primary_node", constants.QFT_TEXT), IQ_CONFIG,
953
     lambda ctx, inst: (constants.QRFS_NORMAL, inst.primary_node)),
954
    (_MakeField("snodes", "Secondary_Nodes", constants.QFT_OTHER), IQ_CONFIG,
955
     lambda ctx, inst: (constants.QRFS_NORMAL, list(inst.secondary_nodes))),
956
    (_MakeField("admin_state", "Autostart", constants.QFT_BOOL), IQ_CONFIG,
957
     lambda ctx, inst: (constants.QRFS_NORMAL, inst.admin_up)),
958
    (_MakeField("tags", "Tags", constants.QFT_OTHER), IQ_CONFIG,
959
     lambda ctx, inst: (constants.QRFS_NORMAL, list(inst.GetTags()))),
960
    ]
961

    
962
  # Add simple fields
963
  fields.extend([(_MakeField(name, title, kind), IQ_CONFIG, _GetItemAttr(name))
964
                 for (name, (title, kind)) in _INST_SIMPLE_FIELDS.items()])
965

    
966
  # Fields requiring talking to the node
967
  fields.extend([
968
    (_MakeField("oper_state", "Running", constants.QFT_BOOL), IQ_LIVE,
969
     _GetInstOperState),
970
    (_MakeField("oper_ram", "RuntimeMemory", constants.QFT_UNIT), IQ_LIVE,
971
     _GetInstLiveData("memory")),
972
    (_MakeField("oper_vcpus", "RuntimeVCPUs", constants.QFT_NUMBER), IQ_LIVE,
973
     _GetInstLiveData("vcpus")),
974
    (_MakeField("status", "Status", constants.QFT_TEXT), IQ_LIVE,
975
     _GetInstStatus),
976
    ])
977

    
978
  fields.extend(_GetInstanceParameterFields())
979
  fields.extend(_GetInstanceDiskFields())
980
  fields.extend(_GetInstanceNetworkFields())
981
  fields.extend(_GetItemTimestampFields(IQ_CONFIG))
982

    
983
  return _PrepareFieldList(fields)
984

    
985

    
986
#: Fields available for node queries
987
NODE_FIELDS = _BuildNodeFields()
988

    
989
#: Fields available for instance queries
990
INSTANCE_FIELDS = _BuildInstanceFields()