Statistics
| Branch: | Tag: | Revision:

root / lib / query.py @ a6070ef7

History | View | Annotate | Download (29.3 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 GetQueryResponse(query, ctx):
239
  """Prepares the response for a query.
240

241
  @type query: L{Query}
242
  @param ctx: Data container, see L{Query.Query}
243

244
  """
245
  return objects.QueryResponse(data=query.Query(ctx),
246
                               fields=query.GetFields()).ToDict()
247

    
248

    
249
def QueryFields(fielddefs, selected):
250
  """Returns list of available fields.
251

252
  @type fielddefs: dict
253
  @param fielddefs: Field definitions
254
  @type selected: list of strings
255
  @param selected: List of selected fields
256
  @return: List of L{objects.QueryFieldDefinition}
257

258
  """
259
  if selected is None:
260
    # Client requests all fields, sort by name
261
    fdefs = utils.NiceSort(GetAllFields(fielddefs.values()),
262
                           key=operator.attrgetter("name"))
263
  else:
264
    # Keep order as requested by client
265
    fdefs = Query(fielddefs, selected).GetFields()
266

    
267
  return objects.QueryFieldsResponse(fields=fdefs).ToDict()
268

    
269

    
270
def _MakeField(name, title, kind):
271
  """Wrapper for creating L{objects.QueryFieldDefinition} instances.
272

273
  @param name: Field name as a regular expression
274
  @param title: Human-readable title
275
  @param kind: Field type
276

277
  """
278
  return objects.QueryFieldDefinition(name=name, title=title, kind=kind)
279

    
280

    
281
def _GetNodeRole(node, master_name):
282
  """Determine node role.
283

284
  @type node: L{objects.Node}
285
  @param node: Node object
286
  @type master_name: string
287
  @param master_name: Master node name
288

289
  """
290
  if node.name == master_name:
291
    return "M"
292
  elif node.master_candidate:
293
    return "C"
294
  elif node.drained:
295
    return "D"
296
  elif node.offline:
297
    return "O"
298
  else:
299
    return "R"
300

    
301

    
302
def _GetItemAttr(attr):
303
  """Returns a field function to return an attribute of the item.
304

305
  @param attr: Attribute name
306

307
  """
308
  getter = operator.attrgetter(attr)
309
  return lambda _, item: (constants.QRFS_NORMAL, getter(item))
310

    
311

    
312
def _GetItemTimestamp(getter):
313
  """Returns function for getting timestamp of item.
314

315
  @type getter: callable
316
  @param getter: Function to retrieve timestamp attribute
317

318
  """
319
  def fn(_, item):
320
    """Returns a timestamp of item.
321

322
    """
323
    timestamp = getter(item)
324
    if timestamp is None:
325
      # Old configs might not have all timestamps
326
      return (constants.QRFS_UNAVAIL, None)
327
    else:
328
      return (constants.QRFS_NORMAL, timestamp)
329

    
330
  return fn
331

    
332

    
333
def _GetItemTimestampFields(datatype):
334
  """Returns common timestamp fields.
335

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

338
  """
339
  return [
340
    (_MakeField("ctime", "CTime", constants.QFT_TIMESTAMP), datatype,
341
     _GetItemTimestamp(operator.attrgetter("ctime"))),
342
    (_MakeField("mtime", "MTime", constants.QFT_TIMESTAMP), datatype,
343
     _GetItemTimestamp(operator.attrgetter("mtime"))),
344
    ]
345

    
346

    
347
class NodeQueryData:
348
  """Data container for node data queries.
349

350
  """
351
  def __init__(self, nodes, live_data, master_name, node_to_primary,
352
               node_to_secondary, groups):
353
    """Initializes this class.
354

355
    """
356
    self.nodes = nodes
357
    self.live_data = live_data
358
    self.master_name = master_name
359
    self.node_to_primary = node_to_primary
360
    self.node_to_secondary = node_to_secondary
361
    self.groups = groups
362

    
363
    # Used for individual rows
364
    self.curlive_data = None
365

    
366
  def __iter__(self):
367
    """Iterate over all nodes.
368

369
    This function has side-effects and only one instance of the resulting
370
    generator should be used at a time.
371

372
    """
373
    for node in self.nodes:
374
      if self.live_data:
375
        self.curlive_data = self.live_data.get(node.name, None)
376
      else:
377
        self.curlive_data = None
378
      yield node
379

    
380

    
381
#: Fields that are direct attributes of an L{objects.Node} object
382
_NODE_SIMPLE_FIELDS = {
383
  "drained": ("Drained", constants.QFT_BOOL),
384
  "master_candidate": ("MasterC", constants.QFT_BOOL),
385
  "master_capable": ("MasterCapable", constants.QFT_BOOL),
386
  "name": ("Node", constants.QFT_TEXT),
387
  "offline": ("Offline", constants.QFT_BOOL),
388
  "serial_no": ("SerialNo", constants.QFT_NUMBER),
389
  "uuid": ("UUID", constants.QFT_TEXT),
390
  "vm_capable": ("VMCapable", constants.QFT_BOOL),
391
  }
392

    
393

    
394
#: Fields requiring talking to the node
395
_NODE_LIVE_FIELDS = {
396
  "bootid": ("BootID", constants.QFT_TEXT, "bootid"),
397
  "cnodes": ("CNodes", constants.QFT_NUMBER, "cpu_nodes"),
398
  "csockets": ("CSockets", constants.QFT_NUMBER, "cpu_sockets"),
399
  "ctotal": ("CTotal", constants.QFT_NUMBER, "cpu_total"),
400
  "dfree": ("DFree", constants.QFT_UNIT, "vg_free"),
401
  "dtotal": ("DTotal", constants.QFT_UNIT, "vg_size"),
402
  "mfree": ("MFree", constants.QFT_UNIT, "memory_free"),
403
  "mnode": ("MNode", constants.QFT_UNIT, "memory_dom0"),
404
  "mtotal": ("MTotal", constants.QFT_UNIT, "memory_total"),
405
  }
406

    
407

    
408
def _GetNodeGroup(ctx, node):
409
  """Returns the name of a node's group.
410

411
  @type ctx: L{NodeQueryData}
412
  @type node: L{objects.Node}
413
  @param node: Node object
414

415
  """
416
  ng = ctx.groups.get(node.group, None)
417
  if ng is None:
418
    # Nodes always have a group, or the configuration is corrupt
419
    return (constants.QRFS_UNAVAIL, None)
420

    
421
  return (constants.QRFS_NORMAL, ng.name)
422

    
423

    
424
def _GetLiveNodeField(field, kind, ctx, node):
425
  """Gets the value of a "live" field from L{NodeQueryData}.
426

427
  @param field: Live field name
428
  @param kind: Data kind, one of L{constants.QFT_ALL}
429
  @type ctx: L{NodeQueryData}
430
  @type node: L{objects.Node}
431
  @param node: Node object
432

433
  """
434
  if node.offline:
435
    return (constants.QRFS_OFFLINE, None)
436

    
437
  if not ctx.curlive_data:
438
    return (constants.QRFS_NODATA, None)
439

    
440
  try:
441
    value = ctx.curlive_data[field]
442
  except KeyError:
443
    return (constants.QRFS_UNAVAIL, None)
444

    
445
  if kind == constants.QFT_TEXT:
446
    return (constants.QRFS_NORMAL, value)
447

    
448
  assert kind in (constants.QFT_NUMBER, constants.QFT_UNIT)
449

    
450
  # Try to convert into number
451
  try:
452
    return (constants.QRFS_NORMAL, int(value))
453
  except (ValueError, TypeError):
454
    logging.exception("Failed to convert node field '%s' (value %r) to int",
455
                      value, field)
456
    return (constants.QRFS_UNAVAIL, None)
457

    
458

    
459
def _BuildNodeFields():
460
  """Builds list of fields for node queries.
461

462
  """
463
  fields = [
464
    (_MakeField("pip", "PrimaryIP", constants.QFT_TEXT), NQ_CONFIG,
465
     lambda ctx, node: (constants.QRFS_NORMAL, node.primary_ip)),
466
    (_MakeField("sip", "SecondaryIP", constants.QFT_TEXT), NQ_CONFIG,
467
     lambda ctx, node: (constants.QRFS_NORMAL, node.secondary_ip)),
468
    (_MakeField("tags", "Tags", constants.QFT_OTHER), NQ_CONFIG,
469
     lambda ctx, node: (constants.QRFS_NORMAL, list(node.GetTags()))),
470
    (_MakeField("master", "IsMaster", constants.QFT_BOOL), NQ_CONFIG,
471
     lambda ctx, node: (constants.QRFS_NORMAL, node.name == ctx.master_name)),
472
    (_MakeField("role", "Role", constants.QFT_TEXT), NQ_CONFIG,
473
     lambda ctx, node: (constants.QRFS_NORMAL,
474
                        _GetNodeRole(node, ctx.master_name))),
475
    (_MakeField("group", "Group", constants.QFT_TEXT), NQ_GROUP, _GetNodeGroup),
476
    (_MakeField("group.uuid", "GroupUUID", constants.QFT_TEXT),
477
     NQ_CONFIG, lambda ctx, node: (constants.QRFS_NORMAL, node.group)),
478
    ]
479

    
480
  def _GetLength(getter):
481
    return lambda ctx, node: (constants.QRFS_NORMAL,
482
                              len(getter(ctx)[node.name]))
483

    
484
  def _GetList(getter):
485
    return lambda ctx, node: (constants.QRFS_NORMAL,
486
                              list(getter(ctx)[node.name]))
487

    
488
  # Add fields operating on instance lists
489
  for prefix, titleprefix, getter in \
490
      [("p", "Pri", operator.attrgetter("node_to_primary")),
491
       ("s", "Sec", operator.attrgetter("node_to_secondary"))]:
492
    fields.extend([
493
      (_MakeField("%sinst_cnt" % prefix, "%sinst" % prefix.upper(),
494
                  constants.QFT_NUMBER),
495
       NQ_INST, _GetLength(getter)),
496
      (_MakeField("%sinst_list" % prefix, "%sInstances" % titleprefix,
497
                  constants.QFT_OTHER),
498
       NQ_INST, _GetList(getter)),
499
      ])
500

    
501
  # Add simple fields
502
  fields.extend([(_MakeField(name, title, kind), NQ_CONFIG, _GetItemAttr(name))
503
                 for (name, (title, kind)) in _NODE_SIMPLE_FIELDS.items()])
504

    
505
  # Add fields requiring live data
506
  fields.extend([
507
    (_MakeField(name, title, kind), NQ_LIVE,
508
     compat.partial(_GetLiveNodeField, nfield, kind))
509
    for (name, (title, kind, nfield)) in _NODE_LIVE_FIELDS.items()
510
    ])
511

    
512
  # Add timestamps
513
  fields.extend(_GetItemTimestampFields(NQ_CONFIG))
514

    
515
  return _PrepareFieldList(fields)
516

    
517

    
518
class InstanceQueryData:
519
  """Data container for instance data queries.
520

521
  """
522
  def __init__(self, instances, cluster, disk_usage, offline_nodes, bad_nodes,
523
               live_data):
524
    """Initializes this class.
525

526
    @param instances: List of instance objects
527
    @param cluster: Cluster object
528
    @type disk_usage: dict; instance name as key
529
    @param disk_usage: Per-instance disk usage
530
    @type offline_nodes: list of strings
531
    @param offline_nodes: List of offline nodes
532
    @type bad_nodes: list of strings
533
    @param bad_nodes: List of faulty nodes
534
    @type live_data: dict; instance name as key
535
    @param live_data: Per-instance live data
536

537
    """
538
    assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
539
           "Offline nodes not included in bad nodes"
540
    assert not (set(live_data.keys()) & set(bad_nodes)), \
541
           "Found live data for bad or offline nodes"
542

    
543
    self.instances = instances
544
    self.cluster = cluster
545
    self.disk_usage = disk_usage
546
    self.offline_nodes = offline_nodes
547
    self.bad_nodes = bad_nodes
548
    self.live_data = live_data
549

    
550
    # Used for individual rows
551
    self.inst_hvparams = None
552
    self.inst_beparams = None
553
    self.inst_nicparams = None
554

    
555
  def __iter__(self):
556
    """Iterate over all instances.
557

558
    This function has side-effects and only one instance of the resulting
559
    generator should be used at a time.
560

561
    """
562
    for inst in self.instances:
563
      self.inst_hvparams = self.cluster.FillHV(inst, skip_globals=True)
564
      self.inst_beparams = self.cluster.FillBE(inst)
565
      self.inst_nicparams = [self.cluster.SimpleFillNIC(nic.nicparams)
566
                             for nic in inst.nics]
567

    
568
      yield inst
569

    
570

    
571
def _GetInstOperState(ctx, inst):
572
  """Get instance's operational status.
573

574
  @type ctx: L{InstanceQueryData}
575
  @type inst: L{objects.Instance}
576
  @param inst: Instance object
577

578
  """
579
  # Can't use QRFS_OFFLINE here as it would describe the instance to be offline
580
  # when we actually don't know due to missing data
581
  if inst.primary_node in ctx.bad_nodes:
582
    return (constants.QRFS_NODATA, None)
583
  else:
584
    return (constants.QRFS_NORMAL, bool(ctx.live_data.get(inst.name)))
585

    
586

    
587
def _GetInstLiveData(name):
588
  """Build function for retrieving live data.
589

590
  @type name: string
591
  @param name: Live data field name
592

593
  """
594
  def fn(ctx, inst):
595
    """Get live data for an instance.
596

597
    @type ctx: L{InstanceQueryData}
598
    @type inst: L{objects.Instance}
599
    @param inst: Instance object
600

601
    """
602
    if (inst.primary_node in ctx.bad_nodes or
603
        inst.primary_node in ctx.offline_nodes):
604
      # Can't use QRFS_OFFLINE here as it would describe the instance to be
605
      # offline when we actually don't know due to missing data
606
      return (constants.QRFS_NODATA, None)
607

    
608
    if inst.name in ctx.live_data:
609
      data = ctx.live_data[inst.name]
610
      if name in data:
611
        return (constants.QRFS_NORMAL, data[name])
612

    
613
    return (constants.QRFS_UNAVAIL, None)
614

    
615
  return fn
616

    
617

    
618
def _GetInstStatus(ctx, inst):
619
  """Get instance status.
620

621
  @type ctx: L{InstanceQueryData}
622
  @type inst: L{objects.Instance}
623
  @param inst: Instance object
624

625
  """
626
  if inst.primary_node in ctx.offline_nodes:
627
    return (constants.QRFS_NORMAL, "ERROR_nodeoffline")
628

    
629
  if inst.primary_node in ctx.bad_nodes:
630
    return (constants.QRFS_NORMAL, "ERROR_nodedown")
631

    
632
  if bool(ctx.live_data.get(inst.name)):
633
    if inst.admin_up:
634
      return (constants.QRFS_NORMAL, "running")
635
    else:
636
      return (constants.QRFS_NORMAL, "ERROR_up")
637

    
638
  if inst.admin_up:
639
    return (constants.QRFS_NORMAL, "ERROR_down")
640

    
641
  return (constants.QRFS_NORMAL, "ADMIN_down")
642

    
643

    
644
def _GetInstDiskSize(index):
645
  """Build function for retrieving disk size.
646

647
  @type index: int
648
  @param index: Disk index
649

650
  """
651
  def fn(_, inst):
652
    """Get size of a disk.
653

654
    @type inst: L{objects.Instance}
655
    @param inst: Instance object
656

657
    """
658
    try:
659
      return (constants.QRFS_NORMAL, inst.disks[index].size)
660
    except IndexError:
661
      return (constants.QRFS_UNAVAIL, None)
662

    
663
  return fn
664

    
665

    
666
def _GetInstNic(index, cb):
667
  """Build function for calling another function with an instance NIC.
668

669
  @type index: int
670
  @param index: NIC index
671
  @type cb: callable
672
  @param cb: Callback
673

674
  """
675
  def fn(ctx, inst):
676
    """Call helper function with instance NIC.
677

678
    @type ctx: L{InstanceQueryData}
679
    @type inst: L{objects.Instance}
680
    @param inst: Instance object
681

682
    """
683
    try:
684
      nic = inst.nics[index]
685
    except IndexError:
686
      return (constants.QRFS_UNAVAIL, None)
687

    
688
    return cb(ctx, index, nic)
689

    
690
  return fn
691

    
692

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

696
  @type ctx: L{InstanceQueryData}
697
  @type nic: L{objects.NIC}
698
  @param nic: NIC object
699

700
  """
701
  if nic.ip is None:
702
    return (constants.QRFS_UNAVAIL, None)
703
  else:
704
    return (constants.QRFS_NORMAL, nic.ip)
705

    
706

    
707
def _GetInstNicBridge(ctx, index, _):
708
  """Get a NIC's bridge.
709

710
  @type ctx: L{InstanceQueryData}
711
  @type index: int
712
  @param index: NIC index
713

714
  """
715
  assert len(ctx.inst_nicparams) >= index
716

    
717
  nicparams = ctx.inst_nicparams[index]
718

    
719
  if nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
720
    return (constants.QRFS_NORMAL, nicparams[constants.NIC_LINK])
721
  else:
722
    return (constants.QRFS_UNAVAIL, None)
723

    
724

    
725
def _GetInstAllNicBridges(ctx, inst):
726
  """Get all network bridges for an instance.
727

728
  @type ctx: L{InstanceQueryData}
729
  @type inst: L{objects.Instance}
730
  @param inst: Instance object
731

732
  """
733
  assert len(ctx.inst_nicparams) == len(inst.nics)
734

    
735
  result = []
736

    
737
  for nicp in ctx.inst_nicparams:
738
    if nicp[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
739
      result.append(nicp[constants.NIC_LINK])
740
    else:
741
      result.append(None)
742

    
743
  assert len(result) == len(inst.nics)
744

    
745
  return (constants.QRFS_NORMAL, result)
746

    
747

    
748
def _GetInstNicParam(name):
749
  """Build function for retrieving a NIC parameter.
750

751
  @type name: string
752
  @param name: Parameter name
753

754
  """
755
  def fn(ctx, index, _):
756
    """Get a NIC's bridge.
757

758
    @type ctx: L{InstanceQueryData}
759
    @type inst: L{objects.Instance}
760
    @param inst: Instance object
761
    @type nic: L{objects.NIC}
762
    @param nic: NIC object
763

764
    """
765
    assert len(ctx.inst_nicparams) >= index
766
    return (constants.QRFS_NORMAL, ctx.inst_nicparams[index][name])
767

    
768
  return fn
769

    
770

    
771
def _GetInstanceNetworkFields():
772
  """Get instance fields involving network interfaces.
773

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

776
  """
777
  nic_mac_fn = lambda ctx, _, nic: (constants.QRFS_NORMAL, nic.mac)
778
  nic_mode_fn = _GetInstNicParam(constants.NIC_MODE)
779
  nic_link_fn = _GetInstNicParam(constants.NIC_LINK)
780

    
781
  fields = [
782
    # First NIC (legacy)
783
    (_MakeField("ip", "IP_address", constants.QFT_TEXT), IQ_CONFIG,
784
     _GetInstNic(0, _GetInstNicIp)),
785
    (_MakeField("mac", "MAC_address", constants.QFT_TEXT), IQ_CONFIG,
786
     _GetInstNic(0, nic_mac_fn)),
787
    (_MakeField("bridge", "Bridge", constants.QFT_TEXT), IQ_CONFIG,
788
     _GetInstNic(0, _GetInstNicBridge)),
789
    (_MakeField("nic_mode", "NIC_Mode", constants.QFT_TEXT), IQ_CONFIG,
790
     _GetInstNic(0, nic_mode_fn)),
791
    (_MakeField("nic_link", "NIC_Link", constants.QFT_TEXT), IQ_CONFIG,
792
     _GetInstNic(0, nic_link_fn)),
793

    
794
    # All NICs
795
    (_MakeField("nic.count", "NICs", constants.QFT_NUMBER), IQ_CONFIG,
796
     lambda ctx, inst: (constants.QRFS_NORMAL, len(inst.nics))),
797
    (_MakeField("nic.macs", "NIC_MACs", constants.QFT_OTHER), IQ_CONFIG,
798
     lambda ctx, inst: (constants.QRFS_NORMAL, [nic.mac for nic in inst.nics])),
799
    (_MakeField("nic.ips", "NIC_IPs", constants.QFT_OTHER), IQ_CONFIG,
800
     lambda ctx, inst: (constants.QRFS_NORMAL, [nic.ip for nic in inst.nics])),
801
    (_MakeField("nic.modes", "NIC_modes", constants.QFT_OTHER), IQ_CONFIG,
802
     lambda ctx, inst: (constants.QRFS_NORMAL,
803
                        [nicp[constants.NIC_MODE]
804
                         for nicp in ctx.inst_nicparams])),
805
    (_MakeField("nic.links", "NIC_links", constants.QFT_OTHER), IQ_CONFIG,
806
     lambda ctx, inst: (constants.QRFS_NORMAL,
807
                        [nicp[constants.NIC_LINK]
808
                         for nicp in ctx.inst_nicparams])),
809
    (_MakeField("nic.bridges", "NIC_bridges", constants.QFT_OTHER), IQ_CONFIG,
810
     _GetInstAllNicBridges),
811
    ]
812

    
813
  # NICs by number
814
  for i in range(constants.MAX_NICS):
815
    fields.extend([
816
      (_MakeField("nic.ip/%s" % i, "NicIP/%s" % i, constants.QFT_TEXT),
817
       IQ_CONFIG, _GetInstNic(i, _GetInstNicIp)),
818
      (_MakeField("nic.mac/%s" % i, "NicMAC/%s" % i, constants.QFT_TEXT),
819
       IQ_CONFIG, _GetInstNic(i, nic_mac_fn)),
820
      (_MakeField("nic.mode/%s" % i, "NicMode/%s" % i, constants.QFT_TEXT),
821
       IQ_CONFIG, _GetInstNic(i, nic_mode_fn)),
822
      (_MakeField("nic.link/%s" % i, "NicLink/%s" % i, constants.QFT_TEXT),
823
       IQ_CONFIG, _GetInstNic(i, nic_link_fn)),
824
      (_MakeField("nic.bridge/%s" % i, "NicBridge/%s" % i, constants.QFT_TEXT),
825
       IQ_CONFIG, _GetInstNic(i, _GetInstNicBridge)),
826
      ])
827

    
828
  return fields
829

    
830

    
831
def _GetInstDiskUsage(ctx, inst):
832
  """Get disk usage for an instance.
833

834
  @type ctx: L{InstanceQueryData}
835
  @type inst: L{objects.Instance}
836
  @param inst: Instance object
837

838
  """
839
  usage = ctx.disk_usage[inst.name]
840

    
841
  if usage is None:
842
    usage = 0
843

    
844
  return (constants.QRFS_NORMAL, usage)
845

    
846

    
847
def _GetInstanceDiskFields():
848
  """Get instance fields involving disks.
849

850
  @return: List of field definitions used as input for L{_PrepareFieldList}
851

852
  """
853
  fields = [
854
    (_MakeField("disk_usage", "DiskUsage", constants.QFT_UNIT), IQ_DISKUSAGE,
855
     _GetInstDiskUsage),
856
    (_MakeField("sda_size", "LegacyDisk/0", constants.QFT_UNIT), IQ_CONFIG,
857
     _GetInstDiskSize(0)),
858
    (_MakeField("sdb_size", "LegacyDisk/1", constants.QFT_UNIT), IQ_CONFIG,
859
     _GetInstDiskSize(1)),
860
    (_MakeField("disk.count", "Disks", constants.QFT_NUMBER), IQ_CONFIG,
861
     lambda ctx, inst: (constants.QRFS_NORMAL, len(inst.disks))),
862
    (_MakeField("disk.sizes", "Disk_sizes", constants.QFT_OTHER), IQ_CONFIG,
863
     lambda ctx, inst: (constants.QRFS_NORMAL,
864
                        [disk.size for disk in inst.disks])),
865
    ]
866

    
867
  # Disks by number
868
  fields.extend([
869
    (_MakeField("disk.size/%s" % i, "Disk/%s" % i, constants.QFT_UNIT),
870
     IQ_CONFIG, _GetInstDiskSize(i))
871
    for i in range(constants.MAX_DISKS)
872
    ])
873

    
874
  return fields
875

    
876

    
877
def _GetInstanceParameterFields():
878
  """Get instance fields involving parameters.
879

880
  @return: List of field definitions used as input for L{_PrepareFieldList}
881

882
  """
883
  # TODO: Consider moving titles closer to constants
884
  be_title = {
885
    constants.BE_AUTO_BALANCE: "Auto_balance",
886
    constants.BE_MEMORY: "Configured_memory",
887
    constants.BE_VCPUS: "VCPUs",
888
    }
889

    
890
  hv_title = {
891
    constants.HV_ACPI: "ACPI",
892
    constants.HV_BOOT_ORDER: "Boot_order",
893
    constants.HV_CDROM_IMAGE_PATH: "CDROM_image_path",
894
    constants.HV_DISK_TYPE: "Disk_type",
895
    constants.HV_INITRD_PATH: "Initrd_path",
896
    constants.HV_KERNEL_PATH: "Kernel_path",
897
    constants.HV_NIC_TYPE: "NIC_type",
898
    constants.HV_PAE: "PAE",
899
    constants.HV_VNC_BIND_ADDRESS: "VNC_bind_address",
900
    }
901

    
902
  fields = [
903
    # Filled parameters
904
    (_MakeField("hvparams", "HypervisorParameters", constants.QFT_OTHER),
905
     IQ_CONFIG, lambda ctx, _: (constants.QRFS_NORMAL, ctx.inst_hvparams)),
906
    (_MakeField("beparams", "BackendParameters", constants.QFT_OTHER),
907
     IQ_CONFIG, lambda ctx, _: (constants.QRFS_NORMAL, ctx.inst_beparams)),
908
    (_MakeField("vcpus", "LegacyVCPUs", constants.QFT_NUMBER), IQ_CONFIG,
909
     lambda ctx, _: (constants.QRFS_NORMAL,
910
                     ctx.inst_beparams[constants.BE_VCPUS])),
911

    
912
    # Unfilled parameters
913
    (_MakeField("custom_hvparams", "CustomHypervisorParameters",
914
                constants.QFT_OTHER),
915
     IQ_CONFIG, lambda ctx, inst: (constants.QRFS_NORMAL, inst.hvparams)),
916
    (_MakeField("custom_beparams", "CustomBackendParameters",
917
                constants.QFT_OTHER),
918
     IQ_CONFIG, lambda ctx, inst: (constants.QRFS_NORMAL, inst.beparams)),
919
    (_MakeField("custom_nicparams", "CustomNicParameters",
920
                constants.QFT_OTHER),
921
     IQ_CONFIG, lambda ctx, inst: (constants.QRFS_NORMAL,
922
                                   [nic.nicparams for nic in inst.nics])),
923
    ]
924

    
925
  # HV params
926
  def _GetInstHvParam(name):
927
    return lambda ctx, _: (constants.QRFS_NORMAL,
928
                           ctx.inst_hvparams.get(name, None))
929

    
930
  fields.extend([
931
    # For now all hypervisor parameters are exported as QFT_OTHER
932
    (_MakeField("hv/%s" % name, hv_title.get(name, "hv/%s" % name),
933
                constants.QFT_OTHER),
934
     IQ_CONFIG, _GetInstHvParam(name))
935
    for name in constants.HVS_PARAMETERS
936
    if name not in constants.HVC_GLOBALS
937
    ])
938

    
939
  # BE params
940
  def _GetInstBeParam(name):
941
    return lambda ctx, _: (constants.QRFS_NORMAL,
942
                           ctx.inst_beparams.get(name, None))
943

    
944
  fields.extend([
945
    # For now all backend parameters are exported as QFT_OTHER
946
    (_MakeField("be/%s" % name, be_title.get(name, "be/%s" % name),
947
                constants.QFT_OTHER),
948
     IQ_CONFIG, _GetInstBeParam(name))
949
    for name in constants.BES_PARAMETERS
950
    ])
951

    
952
  return fields
953

    
954

    
955
_INST_SIMPLE_FIELDS = {
956
  "disk_template": ("Disk_template", constants.QFT_TEXT),
957
  "hypervisor": ("Hypervisor", constants.QFT_TEXT),
958
  "name": ("Node", constants.QFT_TEXT),
959
  # Depending on the hypervisor, the port can be None
960
  "network_port": ("Network_port", constants.QFT_OTHER),
961
  "os": ("OS", constants.QFT_TEXT),
962
  "serial_no": ("SerialNo", constants.QFT_NUMBER),
963
  "uuid": ("UUID", constants.QFT_TEXT),
964
  }
965

    
966

    
967
def _BuildInstanceFields():
968
  """Builds list of fields for instance queries.
969

970
  """
971
  fields = [
972
    (_MakeField("pnode", "Primary_node", constants.QFT_TEXT), IQ_CONFIG,
973
     lambda ctx, inst: (constants.QRFS_NORMAL, inst.primary_node)),
974
    (_MakeField("snodes", "Secondary_Nodes", constants.QFT_OTHER), IQ_CONFIG,
975
     lambda ctx, inst: (constants.QRFS_NORMAL, list(inst.secondary_nodes))),
976
    (_MakeField("admin_state", "Autostart", constants.QFT_BOOL), IQ_CONFIG,
977
     lambda ctx, inst: (constants.QRFS_NORMAL, inst.admin_up)),
978
    (_MakeField("tags", "Tags", constants.QFT_OTHER), IQ_CONFIG,
979
     lambda ctx, inst: (constants.QRFS_NORMAL, list(inst.GetTags()))),
980
    ]
981

    
982
  # Add simple fields
983
  fields.extend([(_MakeField(name, title, kind), IQ_CONFIG, _GetItemAttr(name))
984
                 for (name, (title, kind)) in _INST_SIMPLE_FIELDS.items()])
985

    
986
  # Fields requiring talking to the node
987
  fields.extend([
988
    (_MakeField("oper_state", "Running", constants.QFT_BOOL), IQ_LIVE,
989
     _GetInstOperState),
990
    (_MakeField("oper_ram", "RuntimeMemory", constants.QFT_UNIT), IQ_LIVE,
991
     _GetInstLiveData("memory")),
992
    (_MakeField("oper_vcpus", "RuntimeVCPUs", constants.QFT_NUMBER), IQ_LIVE,
993
     _GetInstLiveData("vcpus")),
994
    (_MakeField("status", "Status", constants.QFT_TEXT), IQ_LIVE,
995
     _GetInstStatus),
996
    ])
997

    
998
  fields.extend(_GetInstanceParameterFields())
999
  fields.extend(_GetInstanceDiskFields())
1000
  fields.extend(_GetInstanceNetworkFields())
1001
  fields.extend(_GetItemTimestampFields(IQ_CONFIG))
1002

    
1003
  return _PrepareFieldList(fields)
1004

    
1005

    
1006
#: Fields available for node queries
1007
NODE_FIELDS = _BuildNodeFields()
1008

    
1009
#: Fields available for instance queries
1010
INSTANCE_FIELDS = _BuildInstanceFields()