Statistics
| Branch: | Tag: | Revision:

root / lib / query.py @ 24d16f76

History | View | Annotate | Download (30.7 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
(LQ_MODE,
46
 LQ_OWNER,
47
 LQ_PENDING) = range(10, 13)
48

    
49
FIELD_NAME_RE = re.compile(r"^[a-z0-9/._]+$")
50
TITLE_RE = re.compile(r"^[^\s]+$")
51

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

    
63

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

67
  """
68
  return (constants.QRFS_UNKNOWN, None)
69

    
70

    
71
def _GetQueryFields(fielddefs, selected):
72
  """Calculates the internal list of selected fields.
73

74
  Unknown fields are returned as L{constants.QFT_UNKNOWN}.
75

76
  @type fielddefs: dict
77
  @param fielddefs: Field definitions
78
  @type selected: list of strings
79
  @param selected: List of selected fields
80

81
  """
82
  result = []
83

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

    
91
    assert len(fdef) == 3
92

    
93
    result.append(fdef)
94

    
95
  return result
96

    
97

    
98
def GetAllFields(fielddefs):
99
  """Extract L{objects.QueryFieldDefinition} from field definitions.
100

101
  @rtype: list of L{objects.QueryFieldDefinition}
102

103
  """
104
  return [fdef for (fdef, _, _) in fielddefs]
105

    
106

    
107
class Query:
108
  def __init__(self, fieldlist, selected):
109
    """Initializes this class.
110

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

118
    Users of this class can call L{RequestedData} before preparing the data
119
    container to determine what data is needed.
120

121
    @type fieldlist: dictionary
122
    @param fieldlist: Field definitions
123
    @type selected: list of strings
124
    @param selected: List of selected fields
125

126
    """
127
    self._fields = _GetQueryFields(fieldlist, selected)
128

    
129
  def RequestedData(self):
130
    """Gets requested kinds of data.
131

132
    @rtype: frozenset
133

134
    """
135
    return frozenset(datakind
136
                     for (_, datakind, _) in self._fields
137
                     if datakind is not None)
138

    
139
  def GetFields(self):
140
    """Returns the list of fields for this query.
141

142
    Includes unknown fields.
143

144
    @rtype: List of L{objects.QueryFieldDefinition}
145

146
    """
147
    return GetAllFields(self._fields)
148

    
149
  def Query(self, ctx):
150
    """Execute a query.
151

152
    @param ctx: Data container passed to field retrieval functions, must
153
      support iteration using C{__iter__}
154

155
    """
156
    result = [[fn(ctx, item) for (_, _, fn) in self._fields]
157
              for item in ctx]
158

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

    
166
    return result
167

    
168
  def OldStyleQuery(self, ctx):
169
    """Query with "old" query result format.
170

171
    See L{Query.Query} for arguments.
172

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

    
182
    return [[value for (_, value) in row]
183
            for row in self.Query(ctx)]
184

    
185

    
186
def _VerifyResultRow(fields, row):
187
  """Verifies the contents of a query result row.
188

189
  @type fields: list
190
  @param fields: Field definitions for result
191
  @type row: list of tuples
192
  @param row: Row data
193

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

    
202

    
203
def _PrepareFieldList(fields):
204
  """Prepares field list for use by L{Query}.
205

206
  Converts the list to a dictionary and does some verification.
207

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

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

    
220
  result = {}
221

    
222
  for field in fields:
223
    (fdef, _, fn) = field
224

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

    
232
    result[fdef.name] = field
233

    
234
  assert len(result) == len(fields)
235
  assert compat.all(name == fdef.name
236
                    for (name, (fdef, _, _)) in result.items())
237

    
238
  return result
239

    
240

    
241
def GetQueryResponse(query, ctx):
242
  """Prepares the response for a query.
243

244
  @type query: L{Query}
245
  @param ctx: Data container, see L{Query.Query}
246

247
  """
248
  return objects.QueryResponse(data=query.Query(ctx),
249
                               fields=query.GetFields()).ToDict()
250

    
251

    
252
def QueryFields(fielddefs, selected):
253
  """Returns list of available fields.
254

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

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

    
270
  return objects.QueryFieldsResponse(fields=fdefs).ToDict()
271

    
272

    
273
def _MakeField(name, title, kind):
274
  """Wrapper for creating L{objects.QueryFieldDefinition} instances.
275

276
  @param name: Field name as a regular expression
277
  @param title: Human-readable title
278
  @param kind: Field type
279

280
  """
281
  return objects.QueryFieldDefinition(name=name, title=title, kind=kind)
282

    
283

    
284
def _GetNodeRole(node, master_name):
285
  """Determine node role.
286

287
  @type node: L{objects.Node}
288
  @param node: Node object
289
  @type master_name: string
290
  @param master_name: Master node name
291

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

    
304

    
305
def _GetItemAttr(attr):
306
  """Returns a field function to return an attribute of the item.
307

308
  @param attr: Attribute name
309

310
  """
311
  getter = operator.attrgetter(attr)
312
  return lambda _, item: (constants.QRFS_NORMAL, getter(item))
313

    
314

    
315
def _GetItemTimestamp(getter):
316
  """Returns function for getting timestamp of item.
317

318
  @type getter: callable
319
  @param getter: Function to retrieve timestamp attribute
320

321
  """
322
  def fn(_, item):
323
    """Returns a timestamp of item.
324

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

    
333
  return fn
334

    
335

    
336
def _GetItemTimestampFields(datatype):
337
  """Returns common timestamp fields.
338

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

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

    
349

    
350
class NodeQueryData:
351
  """Data container for node data queries.
352

353
  """
354
  def __init__(self, nodes, live_data, master_name, node_to_primary,
355
               node_to_secondary, groups):
356
    """Initializes this class.
357

358
    """
359
    self.nodes = nodes
360
    self.live_data = live_data
361
    self.master_name = master_name
362
    self.node_to_primary = node_to_primary
363
    self.node_to_secondary = node_to_secondary
364
    self.groups = groups
365

    
366
    # Used for individual rows
367
    self.curlive_data = None
368

    
369
  def __iter__(self):
370
    """Iterate over all nodes.
371

372
    This function has side-effects and only one instance of the resulting
373
    generator should be used at a time.
374

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

    
383

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

    
396

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

    
410

    
411
def _GetNodeGroup(ctx, node):
412
  """Returns the name of a node's group.
413

414
  @type ctx: L{NodeQueryData}
415
  @type node: L{objects.Node}
416
  @param node: Node object
417

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

    
424
  return (constants.QRFS_NORMAL, ng.name)
425

    
426

    
427
def _GetLiveNodeField(field, kind, ctx, node):
428
  """Gets the value of a "live" field from L{NodeQueryData}.
429

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

436
  """
437
  if node.offline:
438
    return (constants.QRFS_OFFLINE, None)
439

    
440
  if not ctx.curlive_data:
441
    return (constants.QRFS_NODATA, None)
442

    
443
  try:
444
    value = ctx.curlive_data[field]
445
  except KeyError:
446
    return (constants.QRFS_UNAVAIL, None)
447

    
448
  if kind == constants.QFT_TEXT:
449
    return (constants.QRFS_NORMAL, value)
450

    
451
  assert kind in (constants.QFT_NUMBER, constants.QFT_UNIT)
452

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

    
461

    
462
def _BuildNodeFields():
463
  """Builds list of fields for node queries.
464

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

    
483
  def _GetLength(getter):
484
    return lambda ctx, node: (constants.QRFS_NORMAL,
485
                              len(getter(ctx)[node.name]))
486

    
487
  def _GetList(getter):
488
    return lambda ctx, node: (constants.QRFS_NORMAL,
489
                              list(getter(ctx)[node.name]))
490

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

    
504
  # Add simple fields
505
  fields.extend([(_MakeField(name, title, kind), NQ_CONFIG, _GetItemAttr(name))
506
                 for (name, (title, kind)) in _NODE_SIMPLE_FIELDS.items()])
507

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

    
515
  # Add timestamps
516
  fields.extend(_GetItemTimestampFields(NQ_CONFIG))
517

    
518
  return _PrepareFieldList(fields)
519

    
520

    
521
class InstanceQueryData:
522
  """Data container for instance data queries.
523

524
  """
525
  def __init__(self, instances, cluster, disk_usage, offline_nodes, bad_nodes,
526
               live_data):
527
    """Initializes this class.
528

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

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

    
546
    self.instances = instances
547
    self.cluster = cluster
548
    self.disk_usage = disk_usage
549
    self.offline_nodes = offline_nodes
550
    self.bad_nodes = bad_nodes
551
    self.live_data = live_data
552

    
553
    # Used for individual rows
554
    self.inst_hvparams = None
555
    self.inst_beparams = None
556
    self.inst_nicparams = None
557

    
558
  def __iter__(self):
559
    """Iterate over all instances.
560

561
    This function has side-effects and only one instance of the resulting
562
    generator should be used at a time.
563

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

    
571
      yield inst
572

    
573

    
574
def _GetInstOperState(ctx, inst):
575
  """Get instance's operational status.
576

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

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

    
589

    
590
def _GetInstLiveData(name):
591
  """Build function for retrieving live data.
592

593
  @type name: string
594
  @param name: Live data field name
595

596
  """
597
  def fn(ctx, inst):
598
    """Get live data for an instance.
599

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

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

    
611
    if inst.name in ctx.live_data:
612
      data = ctx.live_data[inst.name]
613
      if name in data:
614
        return (constants.QRFS_NORMAL, data[name])
615

    
616
    return (constants.QRFS_UNAVAIL, None)
617

    
618
  return fn
619

    
620

    
621
def _GetInstStatus(ctx, inst):
622
  """Get instance status.
623

624
  @type ctx: L{InstanceQueryData}
625
  @type inst: L{objects.Instance}
626
  @param inst: Instance object
627

628
  """
629
  if inst.primary_node in ctx.offline_nodes:
630
    return (constants.QRFS_NORMAL, "ERROR_nodeoffline")
631

    
632
  if inst.primary_node in ctx.bad_nodes:
633
    return (constants.QRFS_NORMAL, "ERROR_nodedown")
634

    
635
  if bool(ctx.live_data.get(inst.name)):
636
    if inst.admin_up:
637
      return (constants.QRFS_NORMAL, "running")
638
    else:
639
      return (constants.QRFS_NORMAL, "ERROR_up")
640

    
641
  if inst.admin_up:
642
    return (constants.QRFS_NORMAL, "ERROR_down")
643

    
644
  return (constants.QRFS_NORMAL, "ADMIN_down")
645

    
646

    
647
def _GetInstDiskSize(index):
648
  """Build function for retrieving disk size.
649

650
  @type index: int
651
  @param index: Disk index
652

653
  """
654
  def fn(_, inst):
655
    """Get size of a disk.
656

657
    @type inst: L{objects.Instance}
658
    @param inst: Instance object
659

660
    """
661
    try:
662
      return (constants.QRFS_NORMAL, inst.disks[index].size)
663
    except IndexError:
664
      return (constants.QRFS_UNAVAIL, None)
665

    
666
  return fn
667

    
668

    
669
def _GetInstNic(index, cb):
670
  """Build function for calling another function with an instance NIC.
671

672
  @type index: int
673
  @param index: NIC index
674
  @type cb: callable
675
  @param cb: Callback
676

677
  """
678
  def fn(ctx, inst):
679
    """Call helper function with instance NIC.
680

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

685
    """
686
    try:
687
      nic = inst.nics[index]
688
    except IndexError:
689
      return (constants.QRFS_UNAVAIL, None)
690

    
691
    return cb(ctx, index, nic)
692

    
693
  return fn
694

    
695

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

699
  @type ctx: L{InstanceQueryData}
700
  @type nic: L{objects.NIC}
701
  @param nic: NIC object
702

703
  """
704
  if nic.ip is None:
705
    return (constants.QRFS_UNAVAIL, None)
706
  else:
707
    return (constants.QRFS_NORMAL, nic.ip)
708

    
709

    
710
def _GetInstNicBridge(ctx, index, _):
711
  """Get a NIC's bridge.
712

713
  @type ctx: L{InstanceQueryData}
714
  @type index: int
715
  @param index: NIC index
716

717
  """
718
  assert len(ctx.inst_nicparams) >= index
719

    
720
  nicparams = ctx.inst_nicparams[index]
721

    
722
  if nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
723
    return (constants.QRFS_NORMAL, nicparams[constants.NIC_LINK])
724
  else:
725
    return (constants.QRFS_UNAVAIL, None)
726

    
727

    
728
def _GetInstAllNicBridges(ctx, inst):
729
  """Get all network bridges for an instance.
730

731
  @type ctx: L{InstanceQueryData}
732
  @type inst: L{objects.Instance}
733
  @param inst: Instance object
734

735
  """
736
  assert len(ctx.inst_nicparams) == len(inst.nics)
737

    
738
  result = []
739

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

    
746
  assert len(result) == len(inst.nics)
747

    
748
  return (constants.QRFS_NORMAL, result)
749

    
750

    
751
def _GetInstNicParam(name):
752
  """Build function for retrieving a NIC parameter.
753

754
  @type name: string
755
  @param name: Parameter name
756

757
  """
758
  def fn(ctx, index, _):
759
    """Get a NIC's bridge.
760

761
    @type ctx: L{InstanceQueryData}
762
    @type inst: L{objects.Instance}
763
    @param inst: Instance object
764
    @type nic: L{objects.NIC}
765
    @param nic: NIC object
766

767
    """
768
    assert len(ctx.inst_nicparams) >= index
769
    return (constants.QRFS_NORMAL, ctx.inst_nicparams[index][name])
770

    
771
  return fn
772

    
773

    
774
def _GetInstanceNetworkFields():
775
  """Get instance fields involving network interfaces.
776

777
  @return: List of field definitions used as input for L{_PrepareFieldList}
778

779
  """
780
  nic_mac_fn = lambda ctx, _, nic: (constants.QRFS_NORMAL, nic.mac)
781
  nic_mode_fn = _GetInstNicParam(constants.NIC_MODE)
782
  nic_link_fn = _GetInstNicParam(constants.NIC_LINK)
783

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

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

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

    
831
  return fields
832

    
833

    
834
def _GetInstDiskUsage(ctx, inst):
835
  """Get disk usage for an instance.
836

837
  @type ctx: L{InstanceQueryData}
838
  @type inst: L{objects.Instance}
839
  @param inst: Instance object
840

841
  """
842
  usage = ctx.disk_usage[inst.name]
843

    
844
  if usage is None:
845
    usage = 0
846

    
847
  return (constants.QRFS_NORMAL, usage)
848

    
849

    
850
def _GetInstanceDiskFields():
851
  """Get instance fields involving disks.
852

853
  @return: List of field definitions used as input for L{_PrepareFieldList}
854

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

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

    
877
  return fields
878

    
879

    
880
def _GetInstanceParameterFields():
881
  """Get instance fields involving parameters.
882

883
  @return: List of field definitions used as input for L{_PrepareFieldList}
884

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

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

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

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

    
928
  # HV params
929
  def _GetInstHvParam(name):
930
    return lambda ctx, _: (constants.QRFS_NORMAL,
931
                           ctx.inst_hvparams.get(name, None))
932

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

    
942
  # BE params
943
  def _GetInstBeParam(name):
944
    return lambda ctx, _: (constants.QRFS_NORMAL,
945
                           ctx.inst_beparams.get(name, None))
946

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

    
955
  return fields
956

    
957

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

    
969

    
970
def _BuildInstanceFields():
971
  """Builds list of fields for instance queries.
972

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

    
985
  # Add simple fields
986
  fields.extend([(_MakeField(name, title, kind), IQ_CONFIG, _GetItemAttr(name))
987
                 for (name, (title, kind)) in _INST_SIMPLE_FIELDS.items()])
988

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

    
1001
  fields.extend(_GetInstanceParameterFields())
1002
  fields.extend(_GetInstanceDiskFields())
1003
  fields.extend(_GetInstanceNetworkFields())
1004
  fields.extend(_GetItemTimestampFields(IQ_CONFIG))
1005

    
1006
  return _PrepareFieldList(fields)
1007

    
1008

    
1009
class LockQueryData:
1010
  """Data container for lock data queries.
1011

1012
  """
1013
  def __init__(self, lockdata):
1014
    """Initializes this class.
1015

1016
    """
1017
    self.lockdata = lockdata
1018

    
1019
  def __iter__(self):
1020
    """Iterate over all locks.
1021

1022
    """
1023
    return iter(self.lockdata)
1024

    
1025

    
1026
def _GetLockOwners(_, data):
1027
  """Returns a sorted list of a lock's current owners.
1028

1029
  """
1030
  (_, _, owners, _) = data
1031

    
1032
  if owners:
1033
    owners = utils.NiceSort(owners)
1034

    
1035
  return (constants.QRFS_NORMAL, owners)
1036

    
1037

    
1038
def _GetLockPending(_, data):
1039
  """Returns a sorted list of a lock's pending acquires.
1040

1041
  """
1042
  (_, _, _, pending) = data
1043

    
1044
  if pending:
1045
    pending = [(mode, utils.NiceSort(names))
1046
               for (mode, names) in pending]
1047

    
1048
  return (constants.QRFS_NORMAL, pending)
1049

    
1050

    
1051
def _BuildLockFields():
1052
  """Builds list of fields for lock queries.
1053

1054
  """
1055
  return _PrepareFieldList([
1056
    (_MakeField("name", "Name", constants.QFT_TEXT), None,
1057
     lambda ctx, (name, mode, owners, pending): (constants.QRFS_NORMAL, name)),
1058
    (_MakeField("mode", "Mode", constants.QFT_OTHER), LQ_MODE,
1059
     lambda ctx, (name, mode, owners, pending): (constants.QRFS_NORMAL, mode)),
1060
    (_MakeField("owner", "Owner", constants.QFT_OTHER), LQ_OWNER,
1061
     _GetLockOwners),
1062
    (_MakeField("pending", "Pending", constants.QFT_OTHER), LQ_PENDING,
1063
     _GetLockPending),
1064
    ])
1065

    
1066

    
1067
#: Fields available for node queries
1068
NODE_FIELDS = _BuildNodeFields()
1069

    
1070
#: Fields available for instance queries
1071
INSTANCE_FIELDS = _BuildInstanceFields()
1072

    
1073
#: Fields available for lock queries
1074
LOCK_FIELDS = _BuildLockFields()