Package ganeti :: Module query
[hide private]
[frames] | no frames]

Source Code for Module ganeti.query

   1  # 
   2  # 
   3   
   4  # Copyright (C) 2010, 2011, 2012, 2013 Google Inc. 
   5  # All rights reserved. 
   6  # 
   7  # Redistribution and use in source and binary forms, with or without 
   8  # modification, are permitted provided that the following conditions are 
   9  # met: 
  10  # 
  11  # 1. Redistributions of source code must retain the above copyright notice, 
  12  # this list of conditions and the following disclaimer. 
  13  # 
  14  # 2. Redistributions in binary form must reproduce the above copyright 
  15  # notice, this list of conditions and the following disclaimer in the 
  16  # documentation and/or other materials provided with the distribution. 
  17  # 
  18  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
  19  # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
  20  # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
  21  # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
  22  # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
  23  # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
  24  # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
  25  # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
  26  # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
  27  # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
  28  # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
  29   
  30   
  31  """Module for query operations 
  32   
  33  How it works: 
  34   
  35    - Add field definitions 
  36      - See how L{NODE_FIELDS} is built 
  37      - Each field gets: 
  38        - Query field definition (L{objects.QueryFieldDefinition}, use 
  39          L{_MakeField} for creating), containing: 
  40            - Name, must be lowercase and match L{FIELD_NAME_RE} 
  41            - Title for tables, must not contain whitespace and match 
  42              L{TITLE_RE} 
  43            - Value data type, e.g. L{constants.QFT_NUMBER} 
  44            - Human-readable description, must not end with punctuation or 
  45              contain newlines 
  46        - Data request type, see e.g. C{NQ_*} 
  47        - OR-ed flags, see C{QFF_*} 
  48        - A retrieval function, see L{Query.__init__} for description 
  49      - Pass list of fields through L{_PrepareFieldList} for preparation and 
  50        checks 
  51    - Instantiate L{Query} with prepared field list definition and selected fields 
  52    - Call L{Query.RequestedData} to determine what data to collect/compute 
  53    - Call L{Query.Query} or L{Query.OldStyleQuery} with collected data and use 
  54      result 
  55        - Data container must support iteration using C{__iter__} 
  56        - Items are passed to retrieval functions and can have any format 
  57    - Call L{Query.GetFields} to get list of definitions for selected fields 
  58   
  59  @attention: Retrieval functions must be idempotent. They can be called multiple 
  60    times, in any order and any number of times. 
  61   
  62  """ 
  63   
  64  import logging 
  65  import operator 
  66  import re 
  67   
  68  from ganeti import constants 
  69  from ganeti import errors 
  70  from ganeti import utils 
  71  from ganeti import compat 
  72  from ganeti import objects 
  73  from ganeti import ht 
  74  from ganeti import runtime 
  75  from ganeti import qlang 
  76  from ganeti import jstore 
  77  from ganeti.hypervisor import hv_base 
  78   
  79  from ganeti.constants import (QFT_UNKNOWN, QFT_TEXT, QFT_BOOL, QFT_NUMBER, 
  80                                QFT_NUMBER_FLOAT, QFT_UNIT, QFT_TIMESTAMP, 
  81                                QFT_OTHER, RS_NORMAL, RS_UNKNOWN, RS_NODATA, 
  82                                RS_UNAVAIL, RS_OFFLINE) 
  83   
  84  (NETQ_CONFIG, 
  85   NETQ_GROUP, 
  86   NETQ_STATS, 
  87   NETQ_INST) = range(300, 304) 
  88   
  89  # Constants for requesting data from the caller/data provider. Each property 
  90  # collected/computed separately by the data provider should have its own to 
  91  # only collect the requested data and not more. 
  92   
  93  (NQ_CONFIG, 
  94   NQ_INST, 
  95   NQ_LIVE, 
  96   NQ_GROUP, 
  97   NQ_OOB) = range(1, 6) 
  98   
  99  (IQ_CONFIG, 
 100   IQ_LIVE, 
 101   IQ_DISKUSAGE, 
 102   IQ_CONSOLE, 
 103   IQ_NODES, 
 104   IQ_NETWORKS) = range(100, 106) 
 105   
 106  (LQ_MODE, 
 107   LQ_OWNER, 
 108   LQ_PENDING) = range(10, 13) 
 109   
 110  (GQ_CONFIG, 
 111   GQ_NODE, 
 112   GQ_INST, 
 113   GQ_DISKPARAMS) = range(200, 204) 
 114   
 115  (CQ_CONFIG, 
 116   CQ_QUEUE_DRAINED, 
 117   CQ_WATCHER_PAUSE) = range(300, 303) 
 118   
 119  (JQ_ARCHIVED, ) = range(400, 401) 
 120   
 121  # Query field flags 
 122  QFF_HOSTNAME = 0x01 
 123  QFF_IP_ADDRESS = 0x02 
 124  QFF_JOB_ID = 0x04 
 125  QFF_SPLIT_TIMESTAMP = 0x08 
 126  # Next values: 0x10, 0x20, 0x40, 0x80, 0x100, 0x200 
 127  QFF_ALL = (QFF_HOSTNAME | QFF_IP_ADDRESS | QFF_JOB_ID | QFF_SPLIT_TIMESTAMP) 
 128   
 129  FIELD_NAME_RE = re.compile(r"^[a-z0-9/._]+$") 
 130  TITLE_RE = re.compile(r"^[^\s]+$") 
 131  DOC_RE = re.compile(r"^[A-Z].*[^.,?!]$") 
 132   
 133  #: Verification function for each field type 
 134  _VERIFY_FN = { 
 135    QFT_UNKNOWN: ht.TNone, 
 136    QFT_TEXT: ht.TString, 
 137    QFT_BOOL: ht.TBool, 
 138    QFT_NUMBER: ht.TInt, 
 139    QFT_NUMBER_FLOAT: ht.TFloat, 
 140    QFT_UNIT: ht.TInt, 
 141    QFT_TIMESTAMP: ht.TNumber, 
 142    QFT_OTHER: lambda _: True, 
 143    } 
 144   
 145  # Unique objects for special field statuses 
 146  _FS_UNKNOWN = object() 
 147  _FS_NODATA = object() 
 148  _FS_UNAVAIL = object() 
 149  _FS_OFFLINE = object() 
 150   
 151  #: List of all special status 
 152  _FS_ALL = compat.UniqueFrozenset([ 
 153    _FS_UNKNOWN, 
 154    _FS_NODATA, 
 155    _FS_UNAVAIL, 
 156    _FS_OFFLINE, 
 157    ]) 
 158   
 159  #: VType to QFT mapping 
 160  _VTToQFT = { 
 161    # TODO: fix validation of empty strings 
 162    constants.VTYPE_STRING: QFT_OTHER, # since VTYPE_STRINGs can be empty 
 163    constants.VTYPE_MAYBE_STRING: QFT_OTHER, 
 164    constants.VTYPE_BOOL: QFT_BOOL, 
 165    constants.VTYPE_SIZE: QFT_UNIT, 
 166    constants.VTYPE_INT: QFT_NUMBER, 
 167    constants.VTYPE_FLOAT: QFT_NUMBER_FLOAT, 
 168    } 
 169   
 170  _SERIAL_NO_DOC = "%s object serial number, incremented on each modification" 
 171   
 172   
173 -def _GetUnknownField(ctx, item): # pylint: disable=W0613
174 """Gets the contents of an unknown field. 175 176 """ 177 return _FS_UNKNOWN 178 179
180 -def _GetQueryFields(fielddefs, selected):
181 """Calculates the internal list of selected fields. 182 183 Unknown fields are returned as L{constants.QFT_UNKNOWN}. 184 185 @type fielddefs: dict 186 @param fielddefs: Field definitions 187 @type selected: list of strings 188 @param selected: List of selected fields 189 190 """ 191 result = [] 192 193 for name in selected: 194 try: 195 fdef = fielddefs[name] 196 except KeyError: 197 fdef = (_MakeField(name, name, QFT_UNKNOWN, "Unknown field '%s'" % name), 198 None, 0, _GetUnknownField) 199 200 assert len(fdef) == 4 201 202 result.append(fdef) 203 204 return result
205 206
207 -def GetAllFields(fielddefs):
208 """Extract L{objects.QueryFieldDefinition} from field definitions. 209 210 @rtype: list of L{objects.QueryFieldDefinition} 211 212 """ 213 return [fdef for (fdef, _, _, _) in fielddefs]
214 215
216 -class _FilterHints(object):
217 """Class for filter analytics. 218 219 When filters are used, the user of the L{Query} class usually doesn't know 220 exactly which items will be necessary for building the result. It therefore 221 has to prepare and compute the input data for potentially returning 222 everything. 223 224 There are two ways to optimize this. The first, and simpler, is to assign 225 each field a group of data, so that the caller can determine which 226 computations are necessary depending on the data groups requested. The list 227 of referenced groups must also be computed for fields referenced in the 228 filter. 229 230 The second is restricting the items based on a primary key. The primary key 231 is usually a unique name (e.g. a node name). This class extracts all 232 referenced names from a filter. If it encounters any filter condition which 233 disallows such a list to be determined (e.g. a non-equality filter), all 234 names will be requested. 235 236 The end-effect is that any operation other than L{qlang.OP_OR} and 237 L{qlang.OP_EQUAL} will make the query more expensive. 238 239 """
240 - def __init__(self, namefield):
241 """Initializes this class. 242 243 @type namefield: string 244 @param namefield: Field caller is interested in 245 246 """ 247 self._namefield = namefield 248 249 #: Whether all names need to be requested (e.g. if a non-equality operator 250 #: has been used) 251 self._allnames = False 252 253 #: Which names to request 254 self._names = None 255 256 #: Data kinds referenced by the filter (used by L{Query.RequestedData}) 257 self._datakinds = set()
258
259 - def RequestedNames(self):
260 """Returns all requested values. 261 262 Returns C{None} if list of values can't be determined (e.g. encountered 263 non-equality operators). 264 265 @rtype: list 266 267 """ 268 if self._allnames or self._names is None: 269 return None 270 271 return utils.UniqueSequence(self._names)
272
273 - def ReferencedData(self):
274 """Returns all kinds of data referenced by the filter. 275 276 """ 277 return frozenset(self._datakinds)
278
279 - def _NeedAllNames(self):
280 """Changes internal state to request all names. 281 282 """ 283 self._allnames = True 284 self._names = None
285
286 - def NoteLogicOp(self, op):
287 """Called when handling a logic operation. 288 289 @type op: string 290 @param op: Operator 291 292 """ 293 if op != qlang.OP_OR: 294 self._NeedAllNames()
295
296 - def NoteUnaryOp(self, op, datakind): # pylint: disable=W0613
297 """Called when handling an unary operation. 298 299 @type op: string 300 @param op: Operator 301 302 """ 303 if datakind is not None: 304 self._datakinds.add(datakind) 305 306 self._NeedAllNames()
307
308 - def NoteBinaryOp(self, op, datakind, name, value):
309 """Called when handling a binary operation. 310 311 @type op: string 312 @param op: Operator 313 @type name: string 314 @param name: Left-hand side of operator (field name) 315 @param value: Right-hand side of operator 316 317 """ 318 if datakind is not None: 319 self._datakinds.add(datakind) 320 321 if self._allnames: 322 return 323 324 # If any operator other than equality was used, all names need to be 325 # retrieved 326 EQ_OPS = [qlang.OP_EQUAL, qlang.OP_EQUAL_LEGACY] 327 if op in EQ_OPS and name == self._namefield: 328 if self._names is None: 329 self._names = [] 330 self._names.append(value) 331 else: 332 self._NeedAllNames()
333 334
335 -def _WrapLogicOp(op_fn, sentences, ctx, item):
336 """Wrapper for logic operator functions. 337 338 """ 339 return op_fn(fn(ctx, item) for fn in sentences)
340 341
342 -def _WrapUnaryOp(op_fn, inner, ctx, item):
343 """Wrapper for unary operator functions. 344 345 """ 346 return op_fn(inner(ctx, item))
347 348
349 -def _WrapBinaryOp(op_fn, retrieval_fn, value, ctx, item):
350 """Wrapper for binary operator functions. 351 352 """ 353 return op_fn(retrieval_fn(ctx, item), value)
354 355
356 -def _WrapNot(fn, lhs, rhs):
357 """Negates the result of a wrapped function. 358 359 """ 360 return not fn(lhs, rhs)
361 362
363 -def _PrepareRegex(pattern):
364 """Compiles a regular expression. 365 366 """ 367 try: 368 return re.compile(pattern) 369 except re.error, err: 370 raise errors.ParameterError("Invalid regex pattern (%s)" % err)
371 372
373 -def _PrepareSplitTimestamp(value):
374 """Prepares a value for comparison by L{_MakeSplitTimestampComparison}. 375 376 """ 377 if ht.TNumber(value): 378 return value 379 else: 380 return utils.MergeTime(value)
381 382
383 -def _MakeSplitTimestampComparison(fn):
384 """Compares split timestamp values after converting to float. 385 386 """ 387 return lambda lhs, rhs: fn(utils.MergeTime(lhs), rhs)
388 389
390 -def _MakeComparisonChecks(fn):
391 """Prepares flag-specific comparisons using a comparison function. 392 393 """ 394 return [ 395 (QFF_SPLIT_TIMESTAMP, _MakeSplitTimestampComparison(fn), 396 _PrepareSplitTimestamp), 397 (QFF_JOB_ID, lambda lhs, rhs: fn(jstore.ParseJobId(lhs), rhs), 398 jstore.ParseJobId), 399 (None, fn, None), 400 ]
401 402
403 -class _FilterCompilerHelper(object):
404 """Converts a query filter to a callable usable for filtering. 405 406 """ 407 # String statement has no effect, pylint: disable=W0105 408 409 #: How deep filters can be nested 410 _LEVELS_MAX = 10 411 412 # Unique identifiers for operator groups 413 (_OPTYPE_LOGIC, 414 _OPTYPE_UNARY, 415 _OPTYPE_BINARY) = range(1, 4) 416 417 """Functions for equality checks depending on field flags. 418 419 List of tuples containing flags and a callable receiving the left- and 420 right-hand side of the operator. The flags are an OR-ed value of C{QFF_*} 421 (e.g. L{QFF_HOSTNAME} or L{QFF_SPLIT_TIMESTAMP}). 422 423 Order matters. The first item with flags will be used. Flags are checked 424 using binary AND. 425 426 """ 427 _EQUALITY_CHECKS = [ 428 (QFF_HOSTNAME, 429 lambda lhs, rhs: utils.MatchNameComponent(rhs, [lhs], 430 case_sensitive=False), 431 None), 432 (QFF_SPLIT_TIMESTAMP, _MakeSplitTimestampComparison(operator.eq), 433 _PrepareSplitTimestamp), 434 (None, operator.eq, None), 435 ] 436 437 """Known operators 438 439 Operator as key (C{qlang.OP_*}), value a tuple of operator group 440 (C{_OPTYPE_*}) and a group-specific value: 441 442 - C{_OPTYPE_LOGIC}: Callable taking any number of arguments; used by 443 L{_HandleLogicOp} 444 - C{_OPTYPE_UNARY}: Always C{None}; details handled by L{_HandleUnaryOp} 445 - C{_OPTYPE_BINARY}: Callable taking exactly two parameters, the left- and 446 right-hand side of the operator, used by L{_HandleBinaryOp} 447 448 """ 449 _OPS = { 450 # Logic operators 451 qlang.OP_OR: (_OPTYPE_LOGIC, compat.any), 452 qlang.OP_AND: (_OPTYPE_LOGIC, compat.all), 453 454 # Unary operators 455 qlang.OP_NOT: (_OPTYPE_UNARY, None), 456 qlang.OP_TRUE: (_OPTYPE_UNARY, None), 457 458 # Binary operators 459 qlang.OP_EQUAL: (_OPTYPE_BINARY, _EQUALITY_CHECKS), 460 qlang.OP_EQUAL_LEGACY: (_OPTYPE_BINARY, _EQUALITY_CHECKS), 461 qlang.OP_NOT_EQUAL: 462 (_OPTYPE_BINARY, [(flags, compat.partial(_WrapNot, fn), valprepfn) 463 for (flags, fn, valprepfn) in _EQUALITY_CHECKS]), 464 qlang.OP_LT: (_OPTYPE_BINARY, _MakeComparisonChecks(operator.lt)), 465 qlang.OP_LE: (_OPTYPE_BINARY, _MakeComparisonChecks(operator.le)), 466 qlang.OP_GT: (_OPTYPE_BINARY, _MakeComparisonChecks(operator.gt)), 467 qlang.OP_GE: (_OPTYPE_BINARY, _MakeComparisonChecks(operator.ge)), 468 qlang.OP_REGEXP: (_OPTYPE_BINARY, [ 469 (None, lambda lhs, rhs: rhs.search(lhs), _PrepareRegex), 470 ]), 471 qlang.OP_CONTAINS: (_OPTYPE_BINARY, [ 472 (None, operator.contains, None), 473 ]), 474 } 475
476 - def __init__(self, fields):
477 """Initializes this class. 478 479 @param fields: Field definitions (return value of L{_PrepareFieldList}) 480 481 """ 482 self._fields = fields 483 self._hints = None 484 self._op_handler = None
485
486 - def __call__(self, hints, qfilter):
487 """Converts a query filter into a callable function. 488 489 @type hints: L{_FilterHints} or None 490 @param hints: Callbacks doing analysis on filter 491 @type qfilter: list 492 @param qfilter: Filter structure 493 @rtype: callable 494 @return: Function receiving context and item as parameters, returning 495 boolean as to whether item matches filter 496 497 """ 498 self._op_handler = { 499 self._OPTYPE_LOGIC: 500 (self._HandleLogicOp, getattr(hints, "NoteLogicOp", None)), 501 self._OPTYPE_UNARY: 502 (self._HandleUnaryOp, getattr(hints, "NoteUnaryOp", None)), 503 self._OPTYPE_BINARY: 504 (self._HandleBinaryOp, getattr(hints, "NoteBinaryOp", None)), 505 } 506 507 try: 508 filter_fn = self._Compile(qfilter, 0) 509 finally: 510 self._op_handler = None 511 512 return filter_fn
513
514 - def _Compile(self, qfilter, level):
515 """Inner function for converting filters. 516 517 Calls the correct handler functions for the top-level operator. This 518 function is called recursively (e.g. for logic operators). 519 520 """ 521 if not (isinstance(qfilter, (list, tuple)) and qfilter): 522 raise errors.ParameterError("Invalid filter on level %s" % level) 523 524 # Limit recursion 525 if level >= self._LEVELS_MAX: 526 raise errors.ParameterError("Only up to %s levels are allowed (filter" 527 " nested too deep)" % self._LEVELS_MAX) 528 529 # Create copy to be modified 530 operands = qfilter[:] 531 op = operands.pop(0) 532 533 try: 534 (kind, op_data) = self._OPS[op] 535 except KeyError: 536 raise errors.ParameterError("Unknown operator '%s'" % op) 537 538 (handler, hints_cb) = self._op_handler[kind] 539 540 return handler(hints_cb, level, op, op_data, operands)
541
542 - def _LookupField(self, name):
543 """Returns a field definition by name. 544 545 """ 546 try: 547 return self._fields[name] 548 except KeyError: 549 raise errors.ParameterError("Unknown field '%s'" % name)
550
551 - def _HandleLogicOp(self, hints_fn, level, op, op_fn, operands):
552 """Handles logic operators. 553 554 @type hints_fn: callable 555 @param hints_fn: Callback doing some analysis on the filter 556 @type level: integer 557 @param level: Current depth 558 @type op: string 559 @param op: Operator 560 @type op_fn: callable 561 @param op_fn: Function implementing operator 562 @type operands: list 563 @param operands: List of operands 564 565 """ 566 if hints_fn: 567 hints_fn(op) 568 569 return compat.partial(_WrapLogicOp, op_fn, 570 [self._Compile(op, level + 1) for op in operands])
571
572 - def _HandleUnaryOp(self, hints_fn, level, op, op_fn, operands):
573 """Handles unary operators. 574 575 @type hints_fn: callable 576 @param hints_fn: Callback doing some analysis on the filter 577 @type level: integer 578 @param level: Current depth 579 @type op: string 580 @param op: Operator 581 @type op_fn: callable 582 @param op_fn: Function implementing operator 583 @type operands: list 584 @param operands: List of operands 585 586 """ 587 assert op_fn is None 588 589 if len(operands) != 1: 590 raise errors.ParameterError("Unary operator '%s' expects exactly one" 591 " operand" % op) 592 593 if op == qlang.OP_TRUE: 594 (_, datakind, _, retrieval_fn) = self._LookupField(operands[0]) 595 596 if hints_fn: 597 hints_fn(op, datakind) 598 599 op_fn = operator.truth 600 arg = retrieval_fn 601 elif op == qlang.OP_NOT: 602 if hints_fn: 603 hints_fn(op, None) 604 605 op_fn = operator.not_ 606 arg = self._Compile(operands[0], level + 1) 607 else: 608 raise errors.ProgrammerError("Can't handle operator '%s'" % op) 609 610 return compat.partial(_WrapUnaryOp, op_fn, arg)
611
612 - def _HandleBinaryOp(self, hints_fn, level, op, op_data, operands):
613 """Handles binary operators. 614 615 @type hints_fn: callable 616 @param hints_fn: Callback doing some analysis on the filter 617 @type level: integer 618 @param level: Current depth 619 @type op: string 620 @param op: Operator 621 @param op_data: Functions implementing operators 622 @type operands: list 623 @param operands: List of operands 624 625 """ 626 # Unused arguments, pylint: disable=W0613 627 try: 628 (name, value) = operands 629 except (ValueError, TypeError): 630 raise errors.ParameterError("Invalid binary operator, expected exactly" 631 " two operands") 632 633 (fdef, datakind, field_flags, retrieval_fn) = self._LookupField(name) 634 635 assert fdef.kind != QFT_UNKNOWN 636 637 # TODO: Type conversions? 638 639 verify_fn = _VERIFY_FN[fdef.kind] 640 if not verify_fn(value): 641 raise errors.ParameterError("Unable to compare field '%s' (type '%s')" 642 " with '%s', expected %s" % 643 (name, fdef.kind, value.__class__.__name__, 644 verify_fn)) 645 646 if hints_fn: 647 hints_fn(op, datakind, name, value) 648 649 for (fn_flags, fn, valprepfn) in op_data: 650 if fn_flags is None or fn_flags & field_flags: 651 # Prepare value if necessary (e.g. compile regular expression) 652 if valprepfn: 653 value = valprepfn(value) 654 655 return compat.partial(_WrapBinaryOp, fn, retrieval_fn, value) 656 657 raise errors.ProgrammerError("Unable to find operator implementation" 658 " (op '%s', flags %s)" % (op, field_flags))
659 660
661 -def _CompileFilter(fields, hints, qfilter):
662 """Converts a query filter into a callable function. 663 664 See L{_FilterCompilerHelper} for details. 665 666 @rtype: callable 667 668 """ 669 return _FilterCompilerHelper(fields)(hints, qfilter)
670 671
672 -class Query(object):
673 - def __init__(self, fieldlist, selected, qfilter=None, namefield=None):
674 """Initializes this class. 675 676 The field definition is a dictionary with the field's name as a key and a 677 tuple containing, in order, the field definition object 678 (L{objects.QueryFieldDefinition}, the data kind to help calling code 679 collect data and a retrieval function. The retrieval function is called 680 with two parameters, in order, the data container and the item in container 681 (see L{Query.Query}). 682 683 Users of this class can call L{RequestedData} before preparing the data 684 container to determine what data is needed. 685 686 @type fieldlist: dictionary 687 @param fieldlist: Field definitions 688 @type selected: list of strings 689 @param selected: List of selected fields 690 691 """ 692 assert namefield is None or namefield in fieldlist 693 694 self._fields = _GetQueryFields(fieldlist, selected) 695 696 self._filter_fn = None 697 self._requested_names = None 698 self._filter_datakinds = frozenset() 699 700 if qfilter is not None: 701 # Collect requested names if wanted 702 if namefield: 703 hints = _FilterHints(namefield) 704 else: 705 hints = None 706 707 # Build filter function 708 self._filter_fn = _CompileFilter(fieldlist, hints, qfilter) 709 if hints: 710 self._requested_names = hints.RequestedNames() 711 self._filter_datakinds = hints.ReferencedData() 712 713 if namefield is None: 714 self._name_fn = None 715 else: 716 (_, _, _, self._name_fn) = fieldlist[namefield]
717
718 - def RequestedNames(self):
719 """Returns all names referenced in the filter. 720 721 If there is no filter or operators are preventing determining the exact 722 names, C{None} is returned. 723 724 """ 725 return self._requested_names
726
727 - def RequestedData(self):
728 """Gets requested kinds of data. 729 730 @rtype: frozenset 731 732 """ 733 return (self._filter_datakinds | 734 frozenset(datakind for (_, datakind, _, _) in self._fields 735 if datakind is not None))
736
737 - def GetFields(self):
738 """Returns the list of fields for this query. 739 740 Includes unknown fields. 741 742 @rtype: List of L{objects.QueryFieldDefinition} 743 744 """ 745 return GetAllFields(self._fields)
746
747 - def Query(self, ctx, sort_by_name=True):
748 """Execute a query. 749 750 @param ctx: Data container passed to field retrieval functions, must 751 support iteration using C{__iter__} 752 @type sort_by_name: boolean 753 @param sort_by_name: Whether to sort by name or keep the input data's 754 ordering 755 756 """ 757 sort = (self._name_fn and sort_by_name) 758 759 result = [] 760 761 for idx, item in enumerate(ctx): 762 if not (self._filter_fn is None or self._filter_fn(ctx, item)): 763 continue 764 765 row = [_ProcessResult(fn(ctx, item)) for (_, _, _, fn) in self._fields] 766 767 # Verify result 768 if __debug__: 769 _VerifyResultRow(self._fields, row) 770 771 if sort: 772 (status, name) = _ProcessResult(self._name_fn(ctx, item)) 773 assert status == constants.RS_NORMAL 774 # TODO: Are there cases where we wouldn't want to use NiceSort? 775 # Answer: if the name field is non-string... 776 result.append((utils.NiceSortKey(name), idx, row)) 777 else: 778 result.append(row) 779 780 if not sort: 781 return result 782 783 # TODO: Would "heapq" be more efficient than sorting? 784 785 # Sorting in-place instead of using "sorted()" 786 result.sort() 787 788 assert not result or (len(result[0]) == 3 and len(result[-1]) == 3) 789 790 return map(operator.itemgetter(2), result)
791
792 - def OldStyleQuery(self, ctx, sort_by_name=True):
793 """Query with "old" query result format. 794 795 See L{Query.Query} for arguments. 796 797 """ 798 unknown = set(fdef.name for (fdef, _, _, _) in self._fields 799 if fdef.kind == QFT_UNKNOWN) 800 if unknown: 801 raise errors.OpPrereqError("Unknown output fields selected: %s" % 802 (utils.CommaJoin(unknown), ), 803 errors.ECODE_INVAL) 804 805 return [[value for (_, value) in row] 806 for row in self.Query(ctx, sort_by_name=sort_by_name)]
807 808
809 -def _ProcessResult(value):
810 """Converts result values into externally-visible ones. 811 812 """ 813 if value is _FS_UNKNOWN: 814 return (RS_UNKNOWN, None) 815 elif value is _FS_NODATA: 816 return (RS_NODATA, None) 817 elif value is _FS_UNAVAIL: 818 return (RS_UNAVAIL, None) 819 elif value is _FS_OFFLINE: 820 return (RS_OFFLINE, None) 821 else: 822 return (RS_NORMAL, value)
823 824
825 -def _VerifyResultRow(fields, row):
826 """Verifies the contents of a query result row. 827 828 @type fields: list 829 @param fields: Field definitions for result 830 @type row: list of tuples 831 @param row: Row data 832 833 """ 834 assert len(row) == len(fields) 835 errs = [] 836 for ((status, value), (fdef, _, _, _)) in zip(row, fields): 837 if status == RS_NORMAL: 838 if not _VERIFY_FN[fdef.kind](value): 839 errs.append("normal field %s fails validation (value is %s)" % 840 (fdef.name, value)) 841 elif value is not None: 842 errs.append("abnormal field %s has a non-None value" % fdef.name) 843 assert not errs, ("Failed validation: %s in row %s" % 844 (utils.CommaJoin(errs), row))
845 846
847 -def _FieldDictKey((fdef, _, flags, fn)):
848 """Generates key for field dictionary. 849 850 """ 851 assert fdef.name and fdef.title, "Name and title are required" 852 assert FIELD_NAME_RE.match(fdef.name) 853 assert TITLE_RE.match(fdef.title) 854 assert (DOC_RE.match(fdef.doc) and len(fdef.doc.splitlines()) == 1 and 855 fdef.doc.strip() == fdef.doc), \ 856 "Invalid description for field '%s'" % fdef.name 857 assert callable(fn) 858 assert (flags & ~QFF_ALL) == 0, "Unknown flags for field '%s'" % fdef.name 859 860 return fdef.name
861 862
863 -def _PrepareFieldList(fields, aliases):
864 """Prepares field list for use by L{Query}. 865 866 Converts the list to a dictionary and does some verification. 867 868 @type fields: list of tuples; (L{objects.QueryFieldDefinition}, data 869 kind, retrieval function) 870 @param fields: List of fields, see L{Query.__init__} for a better 871 description 872 @type aliases: list of tuples; (alias, target) 873 @param aliases: list of tuples containing aliases; for each 874 alias/target pair, a duplicate will be created in the field list 875 @rtype: dict 876 @return: Field dictionary for L{Query} 877 878 """ 879 if __debug__: 880 duplicates = utils.FindDuplicates(fdef.title.lower() 881 for (fdef, _, _, _) in fields) 882 assert not duplicates, "Duplicate title(s) found: %r" % duplicates 883 884 result = utils.SequenceToDict(fields, key=_FieldDictKey) 885 886 for alias, target in aliases: 887 assert alias not in result, "Alias %s overrides an existing field" % alias 888 assert target in result, "Missing target %s for alias %s" % (target, alias) 889 (fdef, k, flags, fn) = result[target] 890 fdef = fdef.Copy() 891 fdef.name = alias 892 result[alias] = (fdef, k, flags, fn) 893 894 assert len(result) == len(fields) + len(aliases) 895 assert compat.all(name == fdef.name 896 for (name, (fdef, _, _, _)) in result.items()) 897 898 return result
899 900
901 -def GetQueryResponse(query, ctx, sort_by_name=True):
902 """Prepares the response for a query. 903 904 @type query: L{Query} 905 @param ctx: Data container, see L{Query.Query} 906 @type sort_by_name: boolean 907 @param sort_by_name: Whether to sort by name or keep the input data's 908 ordering 909 910 """ 911 return objects.QueryResponse(data=query.Query(ctx, sort_by_name=sort_by_name), 912 fields=query.GetFields()).ToDict()
913 914
915 -def QueryFields(fielddefs, selected):
916 """Returns list of available fields. 917 918 @type fielddefs: dict 919 @param fielddefs: Field definitions 920 @type selected: list of strings 921 @param selected: List of selected fields 922 @return: List of L{objects.QueryFieldDefinition} 923 924 """ 925 if selected is None: 926 # Client requests all fields, sort by name 927 fdefs = utils.NiceSort(GetAllFields(fielddefs.values()), 928 key=operator.attrgetter("name")) 929 else: 930 # Keep order as requested by client 931 fdefs = Query(fielddefs, selected).GetFields() 932 933 return objects.QueryFieldsResponse(fields=fdefs).ToDict()
934 935
936 -def _MakeField(name, title, kind, doc):
937 """Wrapper for creating L{objects.QueryFieldDefinition} instances. 938 939 @param name: Field name as a regular expression 940 @param title: Human-readable title 941 @param kind: Field type 942 @param doc: Human-readable description 943 944 """ 945 return objects.QueryFieldDefinition(name=name, title=title, kind=kind, 946 doc=doc)
947 948
949 -def _StaticValueInner(value, ctx, _): # pylint: disable=W0613
950 """Returns a static value. 951 952 """ 953 return value 954 955
956 -def _StaticValue(value):
957 """Prepares a function to return a static value. 958 959 """ 960 return compat.partial(_StaticValueInner, value)
961 962
963 -def _GetNodeRole(node, master_uuid):
964 """Determine node role. 965 966 @type node: L{objects.Node} 967 @param node: Node object 968 @type master_uuid: string 969 @param master_uuid: Master node UUID 970 971 """ 972 if node.uuid == master_uuid: 973 return constants.NR_MASTER 974 elif node.master_candidate: 975 return constants.NR_MCANDIDATE 976 elif node.drained: 977 return constants.NR_DRAINED 978 elif node.offline: 979 return constants.NR_OFFLINE 980 else: 981 return constants.NR_REGULAR
982 983
984 -def _GetItemAttr(attr):
985 """Returns a field function to return an attribute of the item. 986 987 @param attr: Attribute name 988 989 """ 990 getter = operator.attrgetter(attr) 991 return lambda _, item: getter(item)
992 993
994 -def _GetItemMaybeAttr(attr):
995 """Returns a field function to return a not-None attribute of the item. 996 997 If the value is None, then C{_FS_UNAVAIL} will be returned instead. 998 999 @param attr: Attribute name 1000 1001 """ 1002 def _helper(_, obj): 1003 val = getattr(obj, attr) 1004 if val is None: 1005 return _FS_UNAVAIL 1006 else: 1007 return val
1008 return _helper 1009 1010
1011 -def _GetNDParam(name):
1012 """Return a field function to return an ND parameter out of the context. 1013 1014 """ 1015 def _helper(ctx, _): 1016 if ctx.ndparams is None: 1017 return _FS_UNAVAIL 1018 else: 1019 return ctx.ndparams.get(name, None)
1020 return _helper 1021 1022
1023 -def _BuildNDFields(is_group):
1024 """Builds all the ndparam fields. 1025 1026 @param is_group: whether this is called at group or node level 1027 1028 """ 1029 if is_group: 1030 field_kind = GQ_CONFIG 1031 else: 1032 field_kind = NQ_GROUP 1033 return [(_MakeField("ndp/%s" % name, 1034 constants.NDS_PARAMETER_TITLES.get(name, 1035 "ndp/%s" % name), 1036 _VTToQFT[kind], "The \"%s\" node parameter" % name), 1037 field_kind, 0, _GetNDParam(name)) 1038 for name, kind in constants.NDS_PARAMETER_TYPES.items()]
1039 1040
1041 -def _ConvWrapInner(convert, fn, ctx, item):
1042 """Wrapper for converting values. 1043 1044 @param convert: Conversion function receiving value as single parameter 1045 @param fn: Retrieval function 1046 1047 """ 1048 value = fn(ctx, item) 1049 1050 # Is the value an abnormal status? 1051 if compat.any(value is fs for fs in _FS_ALL): 1052 # Return right away 1053 return value 1054 1055 # TODO: Should conversion function also receive context, item or both? 1056 return convert(value)
1057 1058
1059 -def _ConvWrap(convert, fn):
1060 """Convenience wrapper for L{_ConvWrapInner}. 1061 1062 @param convert: Conversion function receiving value as single parameter 1063 @param fn: Retrieval function 1064 1065 """ 1066 return compat.partial(_ConvWrapInner, convert, fn)
1067 1068
1069 -def _GetItemTimestamp(getter):
1070 """Returns function for getting timestamp of item. 1071 1072 @type getter: callable 1073 @param getter: Function to retrieve timestamp attribute 1074 1075 """ 1076 def fn(_, item): 1077 """Returns a timestamp of item. 1078 1079 """ 1080 timestamp = getter(item) 1081 if timestamp is None: 1082 # Old configs might not have all timestamps 1083 return _FS_UNAVAIL 1084 else: 1085 return timestamp
1086 1087 return fn 1088 1089
1090 -def _GetItemTimestampFields(datatype):
1091 """Returns common timestamp fields. 1092 1093 @param datatype: Field data type for use by L{Query.RequestedData} 1094 1095 """ 1096 return [ 1097 (_MakeField("ctime", "CTime", QFT_TIMESTAMP, "Creation timestamp"), 1098 datatype, 0, _GetItemTimestamp(operator.attrgetter("ctime"))), 1099 (_MakeField("mtime", "MTime", QFT_TIMESTAMP, "Modification timestamp"), 1100 datatype, 0, _GetItemTimestamp(operator.attrgetter("mtime"))), 1101 ]
1102 1103
1104 -class NodeQueryData(object):
1105 """Data container for node data queries. 1106 1107 """
1108 - def __init__(self, nodes, live_data, master_uuid, node_to_primary, 1109 node_to_secondary, inst_uuid_to_inst_name, groups, oob_support, 1110 cluster):
1111 """Initializes this class. 1112 1113 """ 1114 self.nodes = nodes 1115 self.live_data = live_data 1116 self.master_uuid = master_uuid 1117 self.node_to_primary = node_to_primary 1118 self.node_to_secondary = node_to_secondary 1119 self.inst_uuid_to_inst_name = inst_uuid_to_inst_name 1120 self.groups = groups 1121 self.oob_support = oob_support 1122 self.cluster = cluster 1123 1124 # Used for individual rows 1125 self.curlive_data = None 1126 self.ndparams = None
1127
1128 - def __iter__(self):
1129 """Iterate over all nodes. 1130 1131 This function has side-effects and only one instance of the resulting 1132 generator should be used at a time. 1133 1134 """ 1135 for node in self.nodes: 1136 group = self.groups.get(node.group, None) 1137 if group is None: 1138 self.ndparams = None 1139 else: 1140 self.ndparams = self.cluster.FillND(node, group) 1141 if self.live_data: 1142 self.curlive_data = self.live_data.get(node.uuid, None) 1143 else: 1144 self.curlive_data = None 1145 yield node
1146 1147 1148 #: Fields that are direct attributes of an L{objects.Node} object 1149 _NODE_SIMPLE_FIELDS = { 1150 "drained": ("Drained", QFT_BOOL, 0, "Whether node is drained"), 1151 "master_candidate": ("MasterC", QFT_BOOL, 0, 1152 "Whether node is a master candidate"), 1153 "master_capable": ("MasterCapable", QFT_BOOL, 0, 1154 "Whether node can become a master candidate"), 1155 "name": ("Node", QFT_TEXT, QFF_HOSTNAME, "Node name"), 1156 "offline": ("Offline", QFT_BOOL, 0, "Whether node is marked offline"), 1157 "serial_no": ("SerialNo", QFT_NUMBER, 0, _SERIAL_NO_DOC % "Node"), 1158 "uuid": ("UUID", QFT_TEXT, 0, "Node UUID"), 1159 "vm_capable": ("VMCapable", QFT_BOOL, 0, "Whether node can host instances"), 1160 } 1161 1162 1163 #: Fields requiring talking to the node 1164 # Note that none of these are available for non-vm_capable nodes 1165 _NODE_LIVE_FIELDS = { 1166 "bootid": ("BootID", QFT_TEXT, "bootid", 1167 "Random UUID renewed for each system reboot, can be used" 1168 " for detecting reboots by tracking changes"), 1169 "cnodes": ("CNodes", QFT_NUMBER, "cpu_nodes", 1170 "Number of NUMA domains on node (if exported by hypervisor)"), 1171 "cnos": ("CNOs", QFT_NUMBER, "cpu_dom0", 1172 "Number of logical processors used by the node OS (dom0 for Xen)"), 1173 "csockets": ("CSockets", QFT_NUMBER, "cpu_sockets", 1174 "Number of physical CPU sockets (if exported by hypervisor)"), 1175 "ctotal": ("CTotal", QFT_NUMBER, "cpu_total", "Number of logical processors"), 1176 "dfree": ("DFree", QFT_UNIT, "storage_free", 1177 "Available storage space in storage unit"), 1178 "dtotal": ("DTotal", QFT_UNIT, "storage_size", 1179 "Total storage space in storage unit used for instance disk" 1180 " allocation"), 1181 "spfree": ("SpFree", QFT_NUMBER, "spindles_free", 1182 "Available spindles in volume group (exclusive storage only)"), 1183 "sptotal": ("SpTotal", QFT_NUMBER, "spindles_total", 1184 "Total spindles in volume group (exclusive storage only)"), 1185 "mfree": ("MFree", QFT_UNIT, "memory_free", 1186 "Memory available for instance allocations"), 1187 "mnode": ("MNode", QFT_UNIT, "memory_dom0", 1188 "Amount of memory used by node (dom0 for Xen)"), 1189 "mtotal": ("MTotal", QFT_UNIT, "memory_total", 1190 "Total amount of memory of physical machine"), 1191 } 1192 1193
1194 -def _GetGroup(cb):
1195 """Build function for calling another function with an node group. 1196 1197 @param cb: The callback to be called with the nodegroup 1198 1199 """ 1200 def fn(ctx, node): 1201 """Get group data for a node. 1202 1203 @type ctx: L{NodeQueryData} 1204 @type inst: L{objects.Node} 1205 @param inst: Node object 1206 1207 """ 1208 ng = ctx.groups.get(node.group, None) 1209 if ng is None: 1210 # Nodes always have a group, or the configuration is corrupt 1211 return _FS_UNAVAIL 1212 1213 return cb(ctx, node, ng)
1214 1215 return fn 1216 1217
1218 -def _GetNodeGroup(ctx, node, ng): # pylint: disable=W0613
1219 """Returns the name of a node's group. 1220 1221 @type ctx: L{NodeQueryData} 1222 @type node: L{objects.Node} 1223 @param node: Node object 1224 @type ng: L{objects.NodeGroup} 1225 @param ng: The node group this node belongs to 1226 1227 """ 1228 return ng.name 1229 1230
1231 -def _GetNodePower(ctx, node):
1232 """Returns the node powered state 1233 1234 @type ctx: L{NodeQueryData} 1235 @type node: L{objects.Node} 1236 @param node: Node object 1237 1238 """ 1239 if ctx.oob_support[node.uuid]: 1240 return node.powered 1241 1242 return _FS_UNAVAIL
1243 1244
1245 -def _GetNdParams(ctx, node, ng):
1246 """Returns the ndparams for this node. 1247 1248 @type ctx: L{NodeQueryData} 1249 @type node: L{objects.Node} 1250 @param node: Node object 1251 @type ng: L{objects.NodeGroup} 1252 @param ng: The node group this node belongs to 1253 1254 """ 1255 return ctx.cluster.SimpleFillND(ng.FillND(node))
1256 1257
1258 -def _GetLiveNodeField(field, kind, ctx, node):
1259 """Gets the value of a "live" field from L{NodeQueryData}. 1260 1261 @param field: Live field name 1262 @param kind: Data kind, one of L{constants.QFT_ALL} 1263 @type ctx: L{NodeQueryData} 1264 @type node: L{objects.Node} 1265 @param node: Node object 1266 1267 """ 1268 if node.offline: 1269 return _FS_OFFLINE 1270 1271 if not node.vm_capable: 1272 return _FS_UNAVAIL 1273 1274 if not ctx.curlive_data: 1275 return _FS_NODATA 1276 1277 return _GetStatsField(field, kind, ctx.curlive_data)
1278 1279
1280 -def _GetStatsField(field, kind, data):
1281 """Gets a value from live statistics. 1282 1283 If the value is not found, L{_FS_UNAVAIL} is returned. If the field kind is 1284 numeric a conversion to integer is attempted. If that fails, L{_FS_UNAVAIL} 1285 is returned. 1286 1287 @param field: Live field name 1288 @param kind: Data kind, one of L{constants.QFT_ALL} 1289 @type data: dict 1290 @param data: Statistics 1291 1292 """ 1293 try: 1294 value = data[field] 1295 except KeyError: 1296 return _FS_UNAVAIL 1297 1298 if kind == QFT_TEXT: 1299 return value 1300 1301 assert kind in (QFT_NUMBER, QFT_UNIT) 1302 1303 # Try to convert into number 1304 try: 1305 return int(value) 1306 except (ValueError, TypeError): 1307 logging.exception("Failed to convert node field '%s' (value %r) to int", 1308 field, value) 1309 return _FS_UNAVAIL
1310 1311
1312 -def _BuildNodeFields():
1313 """Builds list of fields for node queries. 1314 1315 """ 1316 fields = [ 1317 (_MakeField("pip", "PrimaryIP", QFT_TEXT, "Primary IP address"), 1318 NQ_CONFIG, 0, _GetItemAttr("primary_ip")), 1319 (_MakeField("sip", "SecondaryIP", QFT_TEXT, "Secondary IP address"), 1320 NQ_CONFIG, 0, _GetItemAttr("secondary_ip")), 1321 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), NQ_CONFIG, 0, 1322 lambda ctx, node: list(node.GetTags())), 1323 (_MakeField("master", "IsMaster", QFT_BOOL, "Whether node is master"), 1324 NQ_CONFIG, 0, lambda ctx, node: node.uuid == ctx.master_uuid), 1325 (_MakeField("group", "Group", QFT_TEXT, "Node group"), NQ_GROUP, 0, 1326 _GetGroup(_GetNodeGroup)), 1327 (_MakeField("group.uuid", "GroupUUID", QFT_TEXT, "UUID of node group"), 1328 NQ_CONFIG, 0, _GetItemAttr("group")), 1329 (_MakeField("powered", "Powered", QFT_BOOL, 1330 "Whether node is thought to be powered on"), 1331 NQ_OOB, 0, _GetNodePower), 1332 (_MakeField("ndparams", "NodeParameters", QFT_OTHER, 1333 "Merged node parameters"), 1334 NQ_GROUP, 0, _GetGroup(_GetNdParams)), 1335 (_MakeField("custom_ndparams", "CustomNodeParameters", QFT_OTHER, 1336 "Custom node parameters"), 1337 NQ_GROUP, 0, _GetItemAttr("ndparams")), 1338 # FIXME: The code below return custom hv_state instead of filled one. 1339 # Anyway, this functionality is unlikely to be used. 1340 (_MakeField("hv_state", "HypervisorState", QFT_OTHER, 1341 "Static hypervisor state for default hypervisor only"), 1342 NQ_CONFIG, 0, _GetItemAttr("hv_state_static")), 1343 (_MakeField("custom_hv_state", "CustomHypervisorState", QFT_OTHER, 1344 "Custom static hypervisor state"), 1345 NQ_CONFIG, 0, _GetItemAttr("hv_state_static")), 1346 (_MakeField("disk_state", "DiskState", QFT_OTHER, "Disk state"), 1347 NQ_CONFIG, 0, _GetItemAttr("disk_state_static")), 1348 ] 1349 1350 fields.extend(_BuildNDFields(False)) 1351 1352 # Node role 1353 role_values = (constants.NR_MASTER, constants.NR_MCANDIDATE, 1354 constants.NR_REGULAR, constants.NR_DRAINED, 1355 constants.NR_OFFLINE) 1356 role_doc = ("Node role; \"%s\" for master, \"%s\" for master candidate," 1357 " \"%s\" for regular, \"%s\" for drained, \"%s\" for offline" % 1358 role_values) 1359 fields.append((_MakeField("role", "Role", QFT_TEXT, role_doc), NQ_CONFIG, 0, 1360 lambda ctx, node: _GetNodeRole(node, ctx.master_uuid))) 1361 assert set(role_values) == constants.NR_ALL 1362 1363 def _GetLength(getter): 1364 return lambda ctx, node: len(getter(ctx)[node.uuid])
1365 1366 def _GetList(getter): 1367 return lambda ctx, node: utils.NiceSort( 1368 [ctx.inst_uuid_to_inst_name[uuid] 1369 for uuid in getter(ctx)[node.uuid]]) 1370 1371 # Add fields operating on instance lists 1372 for prefix, titleprefix, docword, getter in \ 1373 [("p", "Pri", "primary", operator.attrgetter("node_to_primary")), 1374 ("s", "Sec", "secondary", operator.attrgetter("node_to_secondary"))]: 1375 # TODO: Allow filterting by hostname in list 1376 fields.extend([ 1377 (_MakeField("%sinst_cnt" % prefix, "%sinst" % prefix.upper(), QFT_NUMBER, 1378 "Number of instances with this node as %s" % docword), 1379 NQ_INST, 0, _GetLength(getter)), 1380 (_MakeField("%sinst_list" % prefix, "%sInstances" % titleprefix, 1381 QFT_OTHER, 1382 "List of instances with this node as %s" % docword), 1383 NQ_INST, 0, _GetList(getter)), 1384 ]) 1385 1386 # Add simple fields 1387 fields.extend([ 1388 (_MakeField(name, title, kind, doc), NQ_CONFIG, flags, _GetItemAttr(name)) 1389 for (name, (title, kind, flags, doc)) in _NODE_SIMPLE_FIELDS.items()]) 1390 1391 # Add fields requiring live data 1392 fields.extend([ 1393 (_MakeField(name, title, kind, doc), NQ_LIVE, 0, 1394 compat.partial(_GetLiveNodeField, nfield, kind)) 1395 for (name, (title, kind, nfield, doc)) in _NODE_LIVE_FIELDS.items()]) 1396 1397 # Add timestamps 1398 fields.extend(_GetItemTimestampFields(NQ_CONFIG)) 1399 1400 return _PrepareFieldList(fields, []) 1401 1402
1403 -class InstanceQueryData(object):
1404 """Data container for instance data queries. 1405 1406 """
1407 - def __init__(self, instances, cluster, disk_usage, offline_node_uuids, 1408 bad_node_uuids, live_data, wrongnode_inst, console, nodes, 1409 groups, networks):
1410 """Initializes this class. 1411 1412 @param instances: List of instance objects 1413 @param cluster: Cluster object 1414 @type disk_usage: dict; instance UUID as key 1415 @param disk_usage: Per-instance disk usage 1416 @type offline_node_uuids: list of strings 1417 @param offline_node_uuids: List of offline nodes 1418 @type bad_node_uuids: list of strings 1419 @param bad_node_uuids: List of faulty nodes 1420 @type live_data: dict; instance UUID as key 1421 @param live_data: Per-instance live data 1422 @type wrongnode_inst: set 1423 @param wrongnode_inst: Set of instances running on wrong node(s) 1424 @type console: dict; instance UUID as key 1425 @param console: Per-instance console information 1426 @type nodes: dict; node UUID as key 1427 @param nodes: Node objects 1428 @type networks: dict; net_uuid as key 1429 @param networks: Network objects 1430 1431 """ 1432 assert len(set(bad_node_uuids) & set(offline_node_uuids)) == \ 1433 len(offline_node_uuids), \ 1434 "Offline nodes not included in bad nodes" 1435 assert not (set(live_data.keys()) & set(bad_node_uuids)), \ 1436 "Found live data for bad or offline nodes" 1437 1438 self.instances = instances 1439 self.cluster = cluster 1440 self.disk_usage = disk_usage 1441 self.offline_nodes = offline_node_uuids 1442 self.bad_nodes = bad_node_uuids 1443 self.live_data = live_data 1444 self.wrongnode_inst = wrongnode_inst 1445 self.console = console 1446 self.nodes = nodes 1447 self.groups = groups 1448 self.networks = networks 1449 1450 # Used for individual rows 1451 self.inst_hvparams = None 1452 self.inst_beparams = None 1453 self.inst_osparams = None 1454 self.inst_nicparams = None
1455
1456 - def __iter__(self):
1457 """Iterate over all instances. 1458 1459 This function has side-effects and only one instance of the resulting 1460 generator should be used at a time. 1461 1462 """ 1463 for inst in self.instances: 1464 self.inst_hvparams = self.cluster.FillHV(inst, skip_globals=True) 1465 self.inst_beparams = self.cluster.FillBE(inst) 1466 self.inst_osparams = self.cluster.SimpleFillOS(inst.os, inst.osparams) 1467 self.inst_nicparams = [self.cluster.SimpleFillNIC(nic.nicparams) 1468 for nic in inst.nics] 1469 1470 yield inst
1471 1472
1473 -def _GetInstOperState(ctx, inst):
1474 """Get instance's operational status. 1475 1476 @type ctx: L{InstanceQueryData} 1477 @type inst: L{objects.Instance} 1478 @param inst: Instance object 1479 1480 """ 1481 # Can't use RS_OFFLINE here as it would describe the instance to 1482 # be offline when we actually don't know due to missing data 1483 if inst.primary_node in ctx.bad_nodes: 1484 return _FS_NODATA 1485 else: 1486 return bool(ctx.live_data.get(inst.uuid))
1487 1488
1489 -def _GetInstLiveData(name):
1490 """Build function for retrieving live data. 1491 1492 @type name: string 1493 @param name: Live data field name 1494 1495 """ 1496 def fn(ctx, inst): 1497 """Get live data for an instance. 1498 1499 @type ctx: L{InstanceQueryData} 1500 @type inst: L{objects.Instance} 1501 @param inst: Instance object 1502 1503 """ 1504 if (inst.primary_node in ctx.bad_nodes or 1505 inst.primary_node in ctx.offline_nodes): 1506 # Can't use RS_OFFLINE here as it would describe the instance to be 1507 # offline when we actually don't know due to missing data 1508 return _FS_NODATA 1509 1510 if inst.uuid in ctx.live_data: 1511 data = ctx.live_data[inst.uuid] 1512 if name in data: 1513 return data[name] 1514 1515 return _FS_UNAVAIL
1516 1517 return fn 1518 1519
1520 -def _GetLiveInstStatus(ctx, instance, instance_state):
1521 hvparams = ctx.cluster.FillHV(instance, skip_globals=True) 1522 1523 allow_userdown = \ 1524 ctx.cluster.enabled_user_shutdown and \ 1525 (instance.hypervisor != constants.HT_KVM or 1526 hvparams[constants.HV_KVM_USER_SHUTDOWN]) 1527 1528 if instance.uuid in ctx.wrongnode_inst: 1529 return constants.INSTST_WRONGNODE 1530 else: 1531 if hv_base.HvInstanceState.IsShutdown(instance_state): 1532 if instance.admin_state == constants.ADMINST_UP and allow_userdown: 1533 return constants.INSTST_USERDOWN 1534 elif instance.admin_state == constants.ADMINST_UP: 1535 return constants.INSTST_ERRORDOWN 1536 else: 1537 return constants.INSTST_ADMINDOWN 1538 else: 1539 if instance.admin_state == constants.ADMINST_UP: 1540 return constants.INSTST_RUNNING 1541 else: 1542 return constants.INSTST_ERRORUP
1543 1544
1545 -def _GetDeadInstStatus(inst):
1546 if inst.admin_state == constants.ADMINST_UP: 1547 return constants.INSTST_ERRORDOWN 1548 elif inst.admin_state == constants.ADMINST_DOWN: 1549 if inst.admin_state_source == constants.USER_SOURCE: 1550 return constants.INSTST_USERDOWN 1551 else: 1552 return constants.INSTST_ADMINDOWN 1553 else: 1554 return constants.INSTST_ADMINOFFLINE
1555 1556
1557 -def _GetInstStatus(ctx, inst):
1558 """Get instance status. 1559 1560 @type ctx: L{InstanceQueryData} 1561 @type inst: L{objects.Instance} 1562 @param inst: Instance object 1563 1564 """ 1565 if inst.primary_node in ctx.offline_nodes: 1566 return constants.INSTST_NODEOFFLINE 1567 1568 if inst.primary_node in ctx.bad_nodes: 1569 return constants.INSTST_NODEDOWN 1570 1571 instance_live_data = ctx.live_data.get(inst.uuid) 1572 1573 if bool(instance_live_data): 1574 return _GetLiveInstStatus(ctx, inst, instance_live_data["state"]) 1575 else: 1576 return _GetDeadInstStatus(inst)
1577 1578
1579 -def _GetInstDisk(index, cb):
1580 """Build function for calling another function with an instance Disk. 1581 1582 @type index: int 1583 @param index: Disk index 1584 @type cb: callable 1585 @param cb: Callback 1586 1587 """ 1588 def fn(ctx, inst): 1589 """Call helper function with instance Disk. 1590 1591 @type ctx: L{InstanceQueryData} 1592 @type inst: L{objects.Instance} 1593 @param inst: Instance object 1594 1595 """ 1596 try: 1597 nic = inst.disks[index] 1598 except IndexError: 1599 return _FS_UNAVAIL 1600 1601 return cb(ctx, index, nic)
1602 1603 return fn 1604 1605
1606 -def _GetInstDiskSize(ctx, _, disk): # pylint: disable=W0613
1607 """Get a Disk's size. 1608 1609 @type ctx: L{InstanceQueryData} 1610 @type disk: L{objects.Disk} 1611 @param disk: The Disk object 1612 1613 """ 1614 if disk.size is None: 1615 return _FS_UNAVAIL 1616 else: 1617 return disk.size 1618 1619
1620 -def _GetInstDiskSpindles(ctx, _, disk): # pylint: disable=W0613
1621 """Get a Disk's spindles. 1622 1623 @type disk: L{objects.Disk} 1624 @param disk: The Disk object 1625 1626 """ 1627 if disk.spindles is None: 1628 return _FS_UNAVAIL 1629 else: 1630 return disk.spindles 1631 1632
1633 -def _GetInstDeviceName(ctx, _, device): # pylint: disable=W0613
1634 """Get a Device's Name. 1635 1636 @type ctx: L{InstanceQueryData} 1637 @type device: L{objects.NIC} or L{objects.Disk} 1638 @param device: The NIC or Disk object 1639 1640 """ 1641 if device.name is None: 1642 return _FS_UNAVAIL 1643 else: 1644 return device.name 1645 1646
1647 -def _GetInstDeviceUUID(ctx, _, device): # pylint: disable=W0613
1648 """Get a Device's UUID. 1649 1650 @type ctx: L{InstanceQueryData} 1651 @type device: L{objects.NIC} or L{objects.Disk} 1652 @param device: The NIC or Disk object 1653 1654 """ 1655 if device.uuid is None: 1656 return _FS_UNAVAIL 1657 else: 1658 return device.uuid 1659 1660
1661 -def _GetInstNic(index, cb):
1662 """Build function for calling another function with an instance NIC. 1663 1664 @type index: int 1665 @param index: NIC index 1666 @type cb: callable 1667 @param cb: Callback 1668 1669 """ 1670 def fn(ctx, inst): 1671 """Call helper function with instance NIC. 1672 1673 @type ctx: L{InstanceQueryData} 1674 @type inst: L{objects.Instance} 1675 @param inst: Instance object 1676 1677 """ 1678 try: 1679 nic = inst.nics[index] 1680 except IndexError: 1681 return _FS_UNAVAIL 1682 1683 return cb(ctx, index, nic)
1684 1685 return fn 1686 1687
1688 -def _GetInstNicNetworkName(ctx, _, nic): # pylint: disable=W0613
1689 """Get a NIC's Network. 1690 1691 @type ctx: L{InstanceQueryData} 1692 @type nic: L{objects.NIC} 1693 @param nic: NIC object 1694 1695 """ 1696 if nic.network is None: 1697 return _FS_UNAVAIL 1698 else: 1699 return ctx.networks[nic.network].name 1700 1701
1702 -def _GetInstNicNetwork(ctx, _, nic): # pylint: disable=W0613
1703 """Get a NIC's Network. 1704 1705 @type ctx: L{InstanceQueryData} 1706 @type nic: L{objects.NIC} 1707 @param nic: NIC object 1708 1709 """ 1710 if nic.network is None: 1711 return _FS_UNAVAIL 1712 else: 1713 return nic.network 1714 1715
1716 -def _GetInstNicIp(ctx, _, nic): # pylint: disable=W0613
1717 """Get a NIC's IP address. 1718 1719 @type ctx: L{InstanceQueryData} 1720 @type nic: L{objects.NIC} 1721 @param nic: NIC object 1722 1723 """ 1724 if nic.ip is None: 1725 return _FS_UNAVAIL 1726 else: 1727 return nic.ip 1728 1729
1730 -def _GetInstNicBridge(ctx, index, _):
1731 """Get a NIC's bridge. 1732 1733 @type ctx: L{InstanceQueryData} 1734 @type index: int 1735 @param index: NIC index 1736 1737 """ 1738 assert len(ctx.inst_nicparams) >= index 1739 1740 nicparams = ctx.inst_nicparams[index] 1741 1742 if nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED: 1743 return nicparams[constants.NIC_LINK] 1744 else: 1745 return _FS_UNAVAIL
1746 1747
1748 -def _GetInstNicVLan(ctx, index, _):
1749 """Get a NIC's VLAN. 1750 1751 @type ctx: L{InstanceQueryData} 1752 @type index: int 1753 @param index: NIC index 1754 1755 """ 1756 assert len(ctx.inst_nicparams) >= index 1757 1758 nicparams = ctx.inst_nicparams[index] 1759 1760 if nicparams[constants.NIC_MODE] == constants.NIC_MODE_OVS: 1761 return nicparams[constants.NIC_VLAN] 1762 else: 1763 return _FS_UNAVAIL
1764 1765
1766 -def _GetInstAllNicNetworkNames(ctx, inst):
1767 """Get all network names for an instance. 1768 1769 @type ctx: L{InstanceQueryData} 1770 @type inst: L{objects.Instance} 1771 @param inst: Instance object 1772 1773 """ 1774 result = [] 1775 1776 for nic in inst.nics: 1777 name = None 1778 if nic.network: 1779 name = ctx.networks[nic.network].name 1780 result.append(name) 1781 1782 assert len(result) == len(inst.nics) 1783 1784 return result
1785 1786
1787 -def _GetInstAllNicVlans(ctx, inst):
1788 """Get all network VLANs for an instance. 1789 1790 @type ctx: L{InstanceQueryData} 1791 @type inst: L{objects.Instance} 1792 @param inst: Instance object 1793 1794 """ 1795 assert len(ctx.inst_nicparams) == len(inst.nics) 1796 1797 result = [] 1798 1799 for nicp in ctx.inst_nicparams: 1800 if nicp[constants.NIC_MODE] in \ 1801 [constants.NIC_MODE_BRIDGED, constants.NIC_MODE_OVS]: 1802 result.append(nicp[constants.NIC_VLAN]) 1803 else: 1804 result.append(None) 1805 1806 assert len(result) == len(inst.nics) 1807 1808 return result
1809 1810
1811 -def _GetInstAllNicBridges(ctx, inst):
1812 """Get all network bridges for an instance. 1813 1814 @type ctx: L{InstanceQueryData} 1815 @type inst: L{objects.Instance} 1816 @param inst: Instance object 1817 1818 """ 1819 assert len(ctx.inst_nicparams) == len(inst.nics) 1820 1821 result = [] 1822 1823 for nicp in ctx.inst_nicparams: 1824 if nicp[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED: 1825 result.append(nicp[constants.NIC_LINK]) 1826 else: 1827 result.append(None) 1828 1829 assert len(result) == len(inst.nics) 1830 1831 return result
1832 1833
1834 -def _GetInstNicParam(name):
1835 """Build function for retrieving a NIC parameter. 1836 1837 @type name: string 1838 @param name: Parameter name 1839 1840 """ 1841 def fn(ctx, index, _): 1842 """Get a NIC's bridge. 1843 1844 @type ctx: L{InstanceQueryData} 1845 @type inst: L{objects.Instance} 1846 @param inst: Instance object 1847 @type nic: L{objects.NIC} 1848 @param nic: NIC object 1849 1850 """ 1851 assert len(ctx.inst_nicparams) >= index 1852 return ctx.inst_nicparams[index][name]
1853 1854 return fn 1855 1856
1857 -def _GetInstanceNetworkFields():
1858 """Get instance fields involving network interfaces. 1859 1860 @return: Tuple containing list of field definitions used as input for 1861 L{_PrepareFieldList} and a list of aliases 1862 1863 """ 1864 nic_mac_fn = lambda ctx, _, nic: nic.mac 1865 nic_mode_fn = _GetInstNicParam(constants.NIC_MODE) 1866 nic_link_fn = _GetInstNicParam(constants.NIC_LINK) 1867 1868 fields = [ 1869 # All NICs 1870 (_MakeField("nic.count", "NICs", QFT_NUMBER, 1871 "Number of network interfaces"), 1872 IQ_CONFIG, 0, lambda ctx, inst: len(inst.nics)), 1873 (_MakeField("nic.macs", "NIC_MACs", QFT_OTHER, 1874 "List containing each network interface's MAC address"), 1875 IQ_CONFIG, 0, lambda ctx, inst: [nic.mac for nic in inst.nics]), 1876 (_MakeField("nic.ips", "NIC_IPs", QFT_OTHER, 1877 "List containing each network interface's IP address"), 1878 IQ_CONFIG, 0, lambda ctx, inst: [nic.ip for nic in inst.nics]), 1879 (_MakeField("nic.names", "NIC_Names", QFT_OTHER, 1880 "List containing each network interface's name"), 1881 IQ_CONFIG, 0, lambda ctx, inst: [nic.name for nic in inst.nics]), 1882 (_MakeField("nic.uuids", "NIC_UUIDs", QFT_OTHER, 1883 "List containing each network interface's UUID"), 1884 IQ_CONFIG, 0, lambda ctx, inst: [nic.uuid for nic in inst.nics]), 1885 (_MakeField("nic.modes", "NIC_modes", QFT_OTHER, 1886 "List containing each network interface's mode"), IQ_CONFIG, 0, 1887 lambda ctx, inst: [nicp[constants.NIC_MODE] 1888 for nicp in ctx.inst_nicparams]), 1889 (_MakeField("nic.links", "NIC_links", QFT_OTHER, 1890 "List containing each network interface's link"), IQ_CONFIG, 0, 1891 lambda ctx, inst: [nicp[constants.NIC_LINK] 1892 for nicp in ctx.inst_nicparams]), 1893 (_MakeField("nic.vlans", "NIC_VLANs", QFT_OTHER, 1894 "List containing each network interface's VLAN"), 1895 IQ_CONFIG, 0, _GetInstAllNicVlans), 1896 (_MakeField("nic.bridges", "NIC_bridges", QFT_OTHER, 1897 "List containing each network interface's bridge"), 1898 IQ_CONFIG, 0, _GetInstAllNicBridges), 1899 (_MakeField("nic.networks", "NIC_networks", QFT_OTHER, 1900 "List containing each interface's network"), IQ_CONFIG, 0, 1901 lambda ctx, inst: [nic.network for nic in inst.nics]), 1902 (_MakeField("nic.networks.names", "NIC_networks_names", QFT_OTHER, 1903 "List containing each interface's network"), 1904 IQ_NETWORKS, 0, _GetInstAllNicNetworkNames) 1905 ] 1906 1907 # NICs by number 1908 for i in range(constants.MAX_NICS): 1909 numtext = utils.FormatOrdinal(i + 1) 1910 fields.extend([ 1911 (_MakeField("nic.ip/%s" % i, "NicIP/%s" % i, QFT_TEXT, 1912 "IP address of %s network interface" % numtext), 1913 IQ_CONFIG, 0, _GetInstNic(i, _GetInstNicIp)), 1914 (_MakeField("nic.mac/%s" % i, "NicMAC/%s" % i, QFT_TEXT, 1915 "MAC address of %s network interface" % numtext), 1916 IQ_CONFIG, 0, _GetInstNic(i, nic_mac_fn)), 1917 (_MakeField("nic.name/%s" % i, "NicName/%s" % i, QFT_TEXT, 1918 "Name address of %s network interface" % numtext), 1919 IQ_CONFIG, 0, _GetInstNic(i, _GetInstDeviceName)), 1920 (_MakeField("nic.uuid/%s" % i, "NicUUID/%s" % i, QFT_TEXT, 1921 "UUID address of %s network interface" % numtext), 1922 IQ_CONFIG, 0, _GetInstNic(i, _GetInstDeviceUUID)), 1923 (_MakeField("nic.mode/%s" % i, "NicMode/%s" % i, QFT_TEXT, 1924 "Mode of %s network interface" % numtext), 1925 IQ_CONFIG, 0, _GetInstNic(i, nic_mode_fn)), 1926 (_MakeField("nic.link/%s" % i, "NicLink/%s" % i, QFT_TEXT, 1927 "Link of %s network interface" % numtext), 1928 IQ_CONFIG, 0, _GetInstNic(i, nic_link_fn)), 1929 (_MakeField("nic.bridge/%s" % i, "NicBridge/%s" % i, QFT_TEXT, 1930 "Bridge of %s network interface" % numtext), 1931 IQ_CONFIG, 0, _GetInstNic(i, _GetInstNicBridge)), 1932 (_MakeField("nic.vlan/%s" % i, "NicVLAN/%s" % i, QFT_TEXT, 1933 "VLAN of %s network interface" % numtext), 1934 IQ_CONFIG, 0, _GetInstNic(i, _GetInstNicVLan)), 1935 (_MakeField("nic.network/%s" % i, "NicNetwork/%s" % i, QFT_TEXT, 1936 "Network of %s network interface" % numtext), 1937 IQ_CONFIG, 0, _GetInstNic(i, _GetInstNicNetwork)), 1938 (_MakeField("nic.network.name/%s" % i, "NicNetworkName/%s" % i, QFT_TEXT, 1939 "Network name of %s network interface" % numtext), 1940 IQ_NETWORKS, 0, _GetInstNic(i, _GetInstNicNetworkName)), 1941 ]) 1942 1943 aliases = [ 1944 # Legacy fields for first NIC 1945 ("ip", "nic.ip/0"), 1946 ("mac", "nic.mac/0"), 1947 ("bridge", "nic.bridge/0"), 1948 ("nic_mode", "nic.mode/0"), 1949 ("nic_link", "nic.link/0"), 1950 ("nic_network", "nic.network/0"), 1951 ] 1952 1953 return (fields, aliases)
1954 1955
1956 -def _GetInstDiskUsage(ctx, inst):
1957 """Get disk usage for an instance. 1958 1959 @type ctx: L{InstanceQueryData} 1960 @type inst: L{objects.Instance} 1961 @param inst: Instance object 1962 1963 """ 1964 usage = ctx.disk_usage[inst.uuid] 1965 1966 if usage is None: 1967 usage = 0 1968 1969 return usage
1970 1971
1972 -def _GetInstanceConsole(ctx, inst):
1973 """Get console information for instance. 1974 1975 @type ctx: L{InstanceQueryData} 1976 @type inst: L{objects.Instance} 1977 @param inst: Instance object 1978 1979 """ 1980 consinfo = ctx.console[inst.uuid] 1981 1982 if consinfo is None: 1983 return _FS_UNAVAIL 1984 1985 return consinfo
1986 1987
1988 -def _GetInstanceDiskFields():
1989 """Get instance fields involving disks. 1990 1991 @return: List of field definitions used as input for L{_PrepareFieldList} 1992 1993 """ 1994 fields = [ 1995 (_MakeField("disk_usage", "DiskUsage", QFT_UNIT, 1996 "Total disk space used by instance on each of its nodes;" 1997 " this is not the disk size visible to the instance, but" 1998 " the usage on the node"), 1999 IQ_DISKUSAGE, 0, _GetInstDiskUsage), 2000 (_MakeField("disk.count", "Disks", QFT_NUMBER, "Number of disks"), 2001 IQ_CONFIG, 0, lambda ctx, inst: len(inst.disks)), 2002 (_MakeField("disk.sizes", "Disk_sizes", QFT_OTHER, "List of disk sizes"), 2003 IQ_CONFIG, 0, lambda ctx, inst: [disk.size for disk in inst.disks]), 2004 (_MakeField("disk.spindles", "Disk_spindles", QFT_OTHER, 2005 "List of disk spindles"), 2006 IQ_CONFIG, 0, lambda ctx, inst: [disk.spindles for disk in inst.disks]), 2007 (_MakeField("disk.names", "Disk_names", QFT_OTHER, "List of disk names"), 2008 IQ_CONFIG, 0, lambda ctx, inst: [disk.name for disk in inst.disks]), 2009 (_MakeField("disk.uuids", "Disk_UUIDs", QFT_OTHER, "List of disk UUIDs"), 2010 IQ_CONFIG, 0, lambda ctx, inst: [disk.uuid for disk in inst.disks]), 2011 ] 2012 2013 # Disks by number 2014 for i in range(constants.MAX_DISKS): 2015 numtext = utils.FormatOrdinal(i + 1) 2016 fields.extend([ 2017 (_MakeField("disk.size/%s" % i, "Disk/%s" % i, QFT_UNIT, 2018 "Disk size of %s disk" % numtext), 2019 IQ_CONFIG, 0, _GetInstDisk(i, _GetInstDiskSize)), 2020 (_MakeField("disk.spindles/%s" % i, "DiskSpindles/%s" % i, QFT_NUMBER, 2021 "Spindles of %s disk" % numtext), 2022 IQ_CONFIG, 0, _GetInstDisk(i, _GetInstDiskSpindles)), 2023 (_MakeField("disk.name/%s" % i, "DiskName/%s" % i, QFT_TEXT, 2024 "Name of %s disk" % numtext), 2025 IQ_CONFIG, 0, _GetInstDisk(i, _GetInstDeviceName)), 2026 (_MakeField("disk.uuid/%s" % i, "DiskUUID/%s" % i, QFT_TEXT, 2027 "UUID of %s disk" % numtext), 2028 IQ_CONFIG, 0, _GetInstDisk(i, _GetInstDeviceUUID))]) 2029 2030 return fields
2031 2032
2033 -def _GetInstanceParameterFields():
2034 """Get instance fields involving parameters. 2035 2036 @return: List of field definitions used as input for L{_PrepareFieldList} 2037 2038 """ 2039 fields = [ 2040 # Filled parameters 2041 (_MakeField("hvparams", "HypervisorParameters", QFT_OTHER, 2042 "Hypervisor parameters (merged)"), 2043 IQ_CONFIG, 0, lambda ctx, _: ctx.inst_hvparams), 2044 (_MakeField("beparams", "BackendParameters", QFT_OTHER, 2045 "Backend parameters (merged)"), 2046 IQ_CONFIG, 0, lambda ctx, _: ctx.inst_beparams), 2047 (_MakeField("osparams", "OpSysParameters", QFT_OTHER, 2048 "Operating system parameters (merged)"), 2049 IQ_CONFIG, 0, lambda ctx, _: ctx.inst_osparams), 2050 2051 # Unfilled parameters 2052 (_MakeField("custom_hvparams", "CustomHypervisorParameters", QFT_OTHER, 2053 "Custom hypervisor parameters"), 2054 IQ_CONFIG, 0, _GetItemAttr("hvparams")), 2055 (_MakeField("custom_beparams", "CustomBackendParameters", QFT_OTHER, 2056 "Custom backend parameters",), 2057 IQ_CONFIG, 0, _GetItemAttr("beparams")), 2058 (_MakeField("custom_osparams", "CustomOpSysParameters", QFT_OTHER, 2059 "Custom operating system parameters",), 2060 IQ_CONFIG, 0, _GetItemAttr("osparams")), 2061 (_MakeField("custom_nicparams", "CustomNicParameters", QFT_OTHER, 2062 "Custom network interface parameters"), 2063 IQ_CONFIG, 0, lambda ctx, inst: [nic.nicparams for nic in inst.nics]), 2064 ] 2065 2066 # HV params 2067 def _GetInstHvParam(name): 2068 return lambda ctx, _: ctx.inst_hvparams.get(name, _FS_UNAVAIL)
2069 2070 fields.extend([ 2071 (_MakeField("hv/%s" % name, 2072 constants.HVS_PARAMETER_TITLES.get(name, "hv/%s" % name), 2073 _VTToQFT[kind], "The \"%s\" hypervisor parameter" % name), 2074 IQ_CONFIG, 0, _GetInstHvParam(name)) 2075 for name, kind in constants.HVS_PARAMETER_TYPES.items() 2076 if name not in constants.HVC_GLOBALS]) 2077 2078 # BE params 2079 def _GetInstBeParam(name): 2080 return lambda ctx, _: ctx.inst_beparams.get(name, None) 2081 2082 fields.extend([ 2083 (_MakeField("be/%s" % name, 2084 constants.BES_PARAMETER_TITLES.get(name, "be/%s" % name), 2085 _VTToQFT[kind], "The \"%s\" backend parameter" % name), 2086 IQ_CONFIG, 0, _GetInstBeParam(name)) 2087 for name, kind in constants.BES_PARAMETER_TYPES.items()]) 2088 2089 return fields 2090 2091 2092 _INST_SIMPLE_FIELDS = { 2093 "disk_template": ("Disk_template", QFT_TEXT, 0, "Instance disk template"), 2094 "hypervisor": ("Hypervisor", QFT_TEXT, 0, "Hypervisor name"), 2095 "name": ("Instance", QFT_TEXT, QFF_HOSTNAME, "Instance name"), 2096 # Depending on the hypervisor, the port can be None 2097 "network_port": ("Network_port", QFT_OTHER, 0, 2098 "Instance network port if available (e.g. for VNC console)"), 2099 "os": ("OS", QFT_TEXT, 0, "Operating system"), 2100 "serial_no": ("SerialNo", QFT_NUMBER, 0, _SERIAL_NO_DOC % "Instance"), 2101 "uuid": ("UUID", QFT_TEXT, 0, "Instance UUID"), 2102 } 2103 2104
2105 -def _GetNodeName(ctx, default, node_uuid):
2106 """Gets node name of a node. 2107 2108 @type ctx: L{InstanceQueryData} 2109 @param default: Default value 2110 @type node_uuid: string 2111 @param node_uuid: Node UUID 2112 2113 """ 2114 try: 2115 node = ctx.nodes[node_uuid] 2116 except KeyError: 2117 return default 2118 else: 2119 return node.name
2120 2121
2122 -def _GetInstNodeGroup(ctx, default, node_uuid):
2123 """Gets group UUID of an instance node. 2124 2125 @type ctx: L{InstanceQueryData} 2126 @param default: Default value 2127 @type node_uuid: string 2128 @param node_uuid: Node UUID 2129 2130 """ 2131 try: 2132 node = ctx.nodes[node_uuid] 2133 except KeyError: 2134 return default 2135 else: 2136 return node.group
2137 2138
2139 -def _GetInstNodeGroupName(ctx, default, node_uuid):
2140 """Gets group name of an instance node. 2141 2142 @type ctx: L{InstanceQueryData} 2143 @param default: Default value 2144 @type node_uuid: string 2145 @param node_uuid: Node UUID 2146 2147 """ 2148 try: 2149 node = ctx.nodes[node_uuid] 2150 except KeyError: 2151 return default 2152 2153 try: 2154 group = ctx.groups[node.group] 2155 except KeyError: 2156 return default 2157 2158 return group.name
2159 2160
2161 -def _BuildInstanceFields():
2162 """Builds list of fields for instance queries. 2163 2164 """ 2165 fields = [ 2166 (_MakeField("pnode", "Primary_node", QFT_TEXT, "Primary node"), 2167 IQ_NODES, QFF_HOSTNAME, 2168 lambda ctx, inst: _GetNodeName(ctx, None, inst.primary_node)), 2169 (_MakeField("pnode.group", "PrimaryNodeGroup", QFT_TEXT, 2170 "Primary node's group"), 2171 IQ_NODES, 0, 2172 lambda ctx, inst: _GetInstNodeGroupName(ctx, _FS_UNAVAIL, 2173 inst.primary_node)), 2174 (_MakeField("pnode.group.uuid", "PrimaryNodeGroupUUID", QFT_TEXT, 2175 "Primary node's group UUID"), 2176 IQ_NODES, 0, 2177 lambda ctx, inst: _GetInstNodeGroup(ctx, _FS_UNAVAIL, inst.primary_node)), 2178 # TODO: Allow filtering by secondary node as hostname 2179 (_MakeField("snodes", "Secondary_Nodes", QFT_OTHER, 2180 "Secondary nodes; usually this will just be one node"), 2181 IQ_NODES, 0, 2182 lambda ctx, inst: map(compat.partial(_GetNodeName, ctx, None), 2183 inst.secondary_nodes)), 2184 (_MakeField("snodes.group", "SecondaryNodesGroups", QFT_OTHER, 2185 "Node groups of secondary nodes"), 2186 IQ_NODES, 0, 2187 lambda ctx, inst: map(compat.partial(_GetInstNodeGroupName, ctx, None), 2188 inst.secondary_nodes)), 2189 (_MakeField("snodes.group.uuid", "SecondaryNodesGroupsUUID", QFT_OTHER, 2190 "Node group UUIDs of secondary nodes"), 2191 IQ_NODES, 0, 2192 lambda ctx, inst: map(compat.partial(_GetInstNodeGroup, ctx, None), 2193 inst.secondary_nodes)), 2194 (_MakeField("admin_state", "InstanceState", QFT_TEXT, 2195 "Desired state of the instance"), 2196 IQ_CONFIG, 0, _GetItemAttr("admin_state")), 2197 (_MakeField("admin_up", "Autostart", QFT_BOOL, 2198 "Desired state of the instance"), 2199 IQ_CONFIG, 0, lambda ctx, inst: inst.admin_state == constants.ADMINST_UP), 2200 (_MakeField("admin_state_source", "InstanceStateSource", QFT_TEXT, 2201 "Who last changed the desired state of the instance"), 2202 IQ_CONFIG, 0, _GetItemAttr("admin_state_source")), 2203 (_MakeField("disks_active", "DisksActive", QFT_BOOL, 2204 "Desired state of the instance disks"), 2205 IQ_CONFIG, 0, _GetItemAttr("disks_active")), 2206 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), IQ_CONFIG, 0, 2207 lambda ctx, inst: list(inst.GetTags())), 2208 (_MakeField("console", "Console", QFT_OTHER, 2209 "Instance console information"), IQ_CONSOLE, 0, 2210 _GetInstanceConsole), 2211 (_MakeField("forthcoming", "Forthcoming", QFT_BOOL, 2212 "Whether the Instance is forthcoming"), IQ_CONFIG, 0, 2213 lambda _, inst: bool(inst.forthcoming)), 2214 ] 2215 2216 # Add simple fields 2217 fields.extend([ 2218 (_MakeField(name, title, kind, doc), IQ_CONFIG, flags, _GetItemAttr(name)) 2219 for (name, (title, kind, flags, doc)) in _INST_SIMPLE_FIELDS.items()]) 2220 2221 # Fields requiring talking to the node 2222 fields.extend([ 2223 (_MakeField("oper_state", "Running", QFT_BOOL, "Actual state of instance"), 2224 IQ_LIVE, 0, _GetInstOperState), 2225 (_MakeField("oper_ram", "Memory", QFT_UNIT, 2226 "Actual memory usage as seen by hypervisor"), 2227 IQ_LIVE, 0, _GetInstLiveData("memory")), 2228 (_MakeField("oper_vcpus", "VCPUs", QFT_NUMBER, 2229 "Actual number of VCPUs as seen by hypervisor"), 2230 IQ_LIVE, 0, _GetInstLiveData("vcpus")), 2231 ]) 2232 2233 # Status field 2234 status_values = (constants.INSTST_RUNNING, constants.INSTST_ADMINDOWN, 2235 constants.INSTST_WRONGNODE, constants.INSTST_ERRORUP, 2236 constants.INSTST_ERRORDOWN, constants.INSTST_NODEDOWN, 2237 constants.INSTST_NODEOFFLINE, constants.INSTST_ADMINOFFLINE, 2238 constants.INSTST_USERDOWN) 2239 status_doc = ("Instance status; \"%s\" if instance is set to be running" 2240 " and actually is, \"%s\" if instance is stopped and" 2241 " is not running, \"%s\" if instance running, but not on its" 2242 " designated primary node, \"%s\" if instance should be" 2243 " stopped, but is actually running, \"%s\" if instance should" 2244 " run, but doesn't, \"%s\" if instance's primary node is down," 2245 " \"%s\" if instance's primary node is marked offline," 2246 " \"%s\" if instance is offline and does not use dynamic," 2247 " \"%s\" if the user shutdown the instance" 2248 " resources" % status_values) 2249 fields.append((_MakeField("status", "Status", QFT_TEXT, status_doc), 2250 IQ_LIVE, 0, _GetInstStatus)) 2251 2252 assert set(status_values) == constants.INSTST_ALL, \ 2253 "Status documentation mismatch" 2254 2255 (network_fields, network_aliases) = _GetInstanceNetworkFields() 2256 2257 fields.extend(network_fields) 2258 fields.extend(_GetInstanceParameterFields()) 2259 fields.extend(_GetInstanceDiskFields()) 2260 fields.extend(_GetItemTimestampFields(IQ_CONFIG)) 2261 2262 aliases = [ 2263 ("vcpus", "be/vcpus"), 2264 ("be/memory", "be/maxmem"), 2265 ("sda_size", "disk.size/0"), 2266 ("sdb_size", "disk.size/1"), 2267 ] + network_aliases 2268 2269 return _PrepareFieldList(fields, aliases)
2270 2271
2272 -class LockQueryData(object):
2273 """Data container for lock data queries. 2274 2275 """
2276 - def __init__(self, lockdata):
2277 """Initializes this class. 2278 2279 """ 2280 self.lockdata = lockdata
2281
2282 - def __iter__(self):
2283 """Iterate over all locks. 2284 2285 """ 2286 return iter(self.lockdata)
2287 2288
2289 -def _GetLockOwners(_, data):
2290 """Returns a sorted list of a lock's current owners. 2291 2292 """ 2293 (_, _, owners, _) = data 2294 2295 if owners: 2296 owners = utils.NiceSort(owners) 2297 2298 return owners
2299 2300
2301 -def _GetLockPending(_, data):
2302 """Returns a sorted list of a lock's pending acquires. 2303 2304 """ 2305 (_, _, _, pending) = data 2306 2307 if pending: 2308 pending = [(mode, utils.NiceSort(names)) 2309 for (mode, names) in pending] 2310 2311 return pending
2312 2313
2314 -def _BuildLockFields():
2315 """Builds list of fields for lock queries. 2316 2317 """ 2318 return _PrepareFieldList([ 2319 # TODO: Lock names are not always hostnames. Should QFF_HOSTNAME be used? 2320 (_MakeField("name", "Name", QFT_TEXT, "Lock name"), None, 0, 2321 lambda ctx, (name, mode, owners, pending): name), 2322 (_MakeField("mode", "Mode", QFT_OTHER, 2323 "Mode in which the lock is currently acquired" 2324 " (exclusive or shared)"), 2325 LQ_MODE, 0, lambda ctx, (name, mode, owners, pending): mode), 2326 (_MakeField("owner", "Owner", QFT_OTHER, "Current lock owner(s)"), 2327 LQ_OWNER, 0, _GetLockOwners), 2328 (_MakeField("pending", "Pending", QFT_OTHER, 2329 "Threads waiting for the lock"), 2330 LQ_PENDING, 0, _GetLockPending), 2331 ], [])
2332 2333
2334 -class GroupQueryData(object):
2335 """Data container for node group data queries. 2336 2337 """
2338 - def __init__(self, cluster, groups, group_to_nodes, group_to_instances, 2339 want_diskparams):
2340 """Initializes this class. 2341 2342 @param cluster: Cluster object 2343 @param groups: List of node group objects 2344 @type group_to_nodes: dict; group UUID as key 2345 @param group_to_nodes: Per-group list of nodes 2346 @type group_to_instances: dict; group UUID as key 2347 @param group_to_instances: Per-group list of (primary) instances 2348 @type want_diskparams: bool 2349 @param want_diskparams: Whether diskparamters should be calculated 2350 2351 """ 2352 self.groups = groups 2353 self.group_to_nodes = group_to_nodes 2354 self.group_to_instances = group_to_instances 2355 self.cluster = cluster 2356 self.want_diskparams = want_diskparams 2357 2358 # Used for individual rows 2359 self.group_ipolicy = None 2360 self.ndparams = None 2361 self.group_dp = None
2362
2363 - def __iter__(self):
2364 """Iterate over all node groups. 2365 2366 This function has side-effects and only one instance of the resulting 2367 generator should be used at a time. 2368 2369 """ 2370 for group in self.groups: 2371 self.group_ipolicy = self.cluster.SimpleFillIPolicy(group.ipolicy) 2372 self.ndparams = self.cluster.SimpleFillND(group.ndparams) 2373 if self.want_diskparams: 2374 self.group_dp = self.cluster.SimpleFillDP(group.diskparams) 2375 else: 2376 self.group_dp = None 2377 yield group
2378 2379 2380 _GROUP_SIMPLE_FIELDS = { 2381 "alloc_policy": ("AllocPolicy", QFT_TEXT, "Allocation policy for group"), 2382 "name": ("Group", QFT_TEXT, "Group name"), 2383 "serial_no": ("SerialNo", QFT_NUMBER, _SERIAL_NO_DOC % "Group"), 2384 "uuid": ("UUID", QFT_TEXT, "Group UUID"), 2385 } 2386 2387
2388 -def _BuildGroupFields():
2389 """Builds list of fields for node group queries. 2390 2391 """ 2392 # Add simple fields 2393 fields = [(_MakeField(name, title, kind, doc), GQ_CONFIG, 0, 2394 _GetItemAttr(name)) 2395 for (name, (title, kind, doc)) in _GROUP_SIMPLE_FIELDS.items()] 2396 2397 def _GetLength(getter): 2398 return lambda ctx, group: len(getter(ctx)[group.uuid])
2399 2400 def _GetSortedList(getter): 2401 return lambda ctx, group: utils.NiceSort(getter(ctx)[group.uuid]) 2402 2403 group_to_nodes = operator.attrgetter("group_to_nodes") 2404 group_to_instances = operator.attrgetter("group_to_instances") 2405 2406 # Add fields for nodes 2407 fields.extend([ 2408 (_MakeField("node_cnt", "Nodes", QFT_NUMBER, "Number of nodes"), 2409 GQ_NODE, 0, _GetLength(group_to_nodes)), 2410 (_MakeField("node_list", "NodeList", QFT_OTHER, "List of nodes"), 2411 GQ_NODE, 0, _GetSortedList(group_to_nodes)), 2412 ]) 2413 2414 # Add fields for instances 2415 fields.extend([ 2416 (_MakeField("pinst_cnt", "Instances", QFT_NUMBER, 2417 "Number of primary instances"), 2418 GQ_INST, 0, _GetLength(group_to_instances)), 2419 (_MakeField("pinst_list", "InstanceList", QFT_OTHER, 2420 "List of primary instances"), 2421 GQ_INST, 0, _GetSortedList(group_to_instances)), 2422 ]) 2423 2424 # Other fields 2425 fields.extend([ 2426 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), GQ_CONFIG, 0, 2427 lambda ctx, group: list(group.GetTags())), 2428 (_MakeField("ipolicy", "InstancePolicy", QFT_OTHER, 2429 "Instance policy limitations (merged)"), 2430 GQ_CONFIG, 0, lambda ctx, _: ctx.group_ipolicy), 2431 (_MakeField("networks", "Networks", QFT_OTHER, 2432 "Node group networks"), 2433 GQ_CONFIG, 0, _GetItemAttr("networks")), 2434 (_MakeField("custom_ipolicy", "CustomInstancePolicy", QFT_OTHER, 2435 "Custom instance policy limitations"), 2436 GQ_CONFIG, 0, _GetItemAttr("ipolicy")), 2437 (_MakeField("custom_ndparams", "CustomNDParams", QFT_OTHER, 2438 "Custom node parameters"), 2439 GQ_CONFIG, 0, _GetItemAttr("ndparams")), 2440 (_MakeField("ndparams", "NDParams", QFT_OTHER, 2441 "Node parameters"), 2442 GQ_CONFIG, 0, lambda ctx, _: ctx.ndparams), 2443 (_MakeField("diskparams", "DiskParameters", QFT_OTHER, 2444 "Disk parameters (merged)"), 2445 GQ_DISKPARAMS, 0, lambda ctx, _: ctx.group_dp), 2446 (_MakeField("custom_diskparams", "CustomDiskParameters", QFT_OTHER, 2447 "Custom disk parameters"), 2448 GQ_CONFIG, 0, _GetItemAttr("diskparams")), 2449 (_MakeField("hv_state", "HypervisorState", QFT_OTHER, 2450 "Custom static hypervisor state"), 2451 GQ_CONFIG, 0, _GetItemAttr("hv_state_static")), 2452 (_MakeField("disk_state", "DiskState", QFT_OTHER, "Disk state"), 2453 GQ_CONFIG, 0, _GetItemAttr("disk_state_static")), 2454 ]) 2455 2456 # ND parameters 2457 fields.extend(_BuildNDFields(True)) 2458 2459 fields.extend(_GetItemTimestampFields(GQ_CONFIG)) 2460 2461 return _PrepareFieldList(fields, []) 2462 2463
2464 -class OsInfo(objects.ConfigObject):
2465 __slots__ = [ 2466 "name", 2467 "valid", 2468 "hidden", 2469 "blacklisted", 2470 "variants", 2471 "api_versions", 2472 "parameters", 2473 "node_status", 2474 "os_hvp", 2475 "osparams", 2476 "trusted" 2477 ]
2478 2479
2480 -def _BuildOsFields():
2481 """Builds list of fields for operating system queries. 2482 2483 """ 2484 fields = [ 2485 (_MakeField("name", "Name", QFT_TEXT, "Operating system name"), 2486 None, 0, _GetItemAttr("name")), 2487 (_MakeField("valid", "Valid", QFT_BOOL, 2488 "Whether operating system definition is valid"), 2489 None, 0, _GetItemAttr("valid")), 2490 (_MakeField("hidden", "Hidden", QFT_BOOL, 2491 "Whether operating system is hidden"), 2492 None, 0, _GetItemAttr("hidden")), 2493 (_MakeField("blacklisted", "Blacklisted", QFT_BOOL, 2494 "Whether operating system is blacklisted"), 2495 None, 0, _GetItemAttr("blacklisted")), 2496 (_MakeField("variants", "Variants", QFT_OTHER, 2497 "Operating system variants"), 2498 None, 0, _ConvWrap(utils.NiceSort, _GetItemAttr("variants"))), 2499 (_MakeField("api_versions", "ApiVersions", QFT_OTHER, 2500 "Operating system API versions"), 2501 None, 0, _ConvWrap(sorted, _GetItemAttr("api_versions"))), 2502 (_MakeField("parameters", "Parameters", QFT_OTHER, 2503 "Operating system parameters"), 2504 None, 0, _ConvWrap(compat.partial(utils.NiceSort, key=compat.fst), 2505 _GetItemAttr("parameters"))), 2506 (_MakeField("node_status", "NodeStatus", QFT_OTHER, 2507 "Status from node"), 2508 None, 0, _GetItemAttr("node_status")), 2509 (_MakeField("os_hvp", "OsHypervisorParams", QFT_OTHER, 2510 "Operating system specific hypervisor parameters"), 2511 None, 0, _GetItemAttr("os_hvp")), 2512 (_MakeField("osparams", "OsParameters", QFT_OTHER, 2513 "Operating system specific parameters"), 2514 None, 0, _GetItemAttr("osparams")), 2515 (_MakeField("trusted", "Trusted", QFT_BOOL, 2516 "Whether this OS is trusted"), 2517 None, 0, _GetItemAttr("trusted")), 2518 ] 2519 2520 return _PrepareFieldList(fields, [])
2521 2522
2523 -class ExtStorageInfo(objects.ConfigObject):
2524 __slots__ = [ 2525 "name", 2526 "node_status", 2527 "nodegroup_status", 2528 "parameters", 2529 ]
2530 2531
2532 -def _BuildExtStorageFields():
2533 """Builds list of fields for extstorage provider queries. 2534 2535 """ 2536 fields = [ 2537 (_MakeField("name", "Name", QFT_TEXT, "ExtStorage provider name"), 2538 None, 0, _GetItemAttr("name")), 2539 (_MakeField("node_status", "NodeStatus", QFT_OTHER, 2540 "Status from node"), 2541 None, 0, _GetItemAttr("node_status")), 2542 (_MakeField("nodegroup_status", "NodegroupStatus", QFT_OTHER, 2543 "Overall Nodegroup status"), 2544 None, 0, _GetItemAttr("nodegroup_status")), 2545 (_MakeField("parameters", "Parameters", QFT_OTHER, 2546 "ExtStorage provider parameters"), 2547 None, 0, _GetItemAttr("parameters")), 2548 ] 2549 2550 return _PrepareFieldList(fields, [])
2551 2552
2553 -def _JobUnavailInner(fn, ctx, (job_id, job)): # pylint: disable=W0613
2554 """Return L{_FS_UNAVAIL} if job is None. 2555 2556 When listing specifc jobs (e.g. "gnt-job list 1 2 3"), a job may not be 2557 found, in which case this function converts it to L{_FS_UNAVAIL}. 2558 2559 """ 2560 if job is None: 2561 return _FS_UNAVAIL 2562 else: 2563 return fn(job) 2564 2565
2566 -def _JobUnavail(inner):
2567 """Wrapper for L{_JobUnavailInner}. 2568 2569 """ 2570 return compat.partial(_JobUnavailInner, inner)
2571 2572
2573 -def _PerJobOpInner(fn, job):
2574 """Executes a function per opcode in a job. 2575 2576 """ 2577 return map(fn, job.ops)
2578 2579
2580 -def _PerJobOp(fn):
2581 """Wrapper for L{_PerJobOpInner}. 2582 2583 """ 2584 return _JobUnavail(compat.partial(_PerJobOpInner, fn))
2585 2586
2587 -def _JobTimestampInner(fn, job):
2588 """Converts unavailable timestamp to L{_FS_UNAVAIL}. 2589 2590 """ 2591 timestamp = fn(job) 2592 2593 if timestamp is None: 2594 return _FS_UNAVAIL 2595 else: 2596 return timestamp
2597 2598
2599 -def _JobTimestamp(fn):
2600 """Wrapper for L{_JobTimestampInner}. 2601 2602 """ 2603 return _JobUnavail(compat.partial(_JobTimestampInner, fn))
2604 2605
2606 -def _BuildJobFields():
2607 """Builds list of fields for job queries. 2608 2609 """ 2610 fields = [ 2611 (_MakeField("id", "ID", QFT_NUMBER, "Job ID"), 2612 None, QFF_JOB_ID, lambda _, (job_id, job): job_id), 2613 (_MakeField("status", "Status", QFT_TEXT, "Job status"), 2614 None, 0, _JobUnavail(lambda job: job.CalcStatus())), 2615 (_MakeField("priority", "Priority", QFT_NUMBER, 2616 ("Current job priority (%s to %s)" % 2617 (constants.OP_PRIO_LOWEST, constants.OP_PRIO_HIGHEST))), 2618 None, 0, _JobUnavail(lambda job: job.CalcPriority())), 2619 (_MakeField("archived", "Archived", QFT_BOOL, "Whether job is archived"), 2620 JQ_ARCHIVED, 0, lambda _, (job_id, job): job.archived), 2621 (_MakeField("ops", "OpCodes", QFT_OTHER, "List of all opcodes"), 2622 None, 0, _PerJobOp(lambda op: op.input.__getstate__())), 2623 (_MakeField("opresult", "OpCode_result", QFT_OTHER, 2624 "List of opcodes results"), 2625 None, 0, _PerJobOp(operator.attrgetter("result"))), 2626 (_MakeField("opstatus", "OpCode_status", QFT_OTHER, 2627 "List of opcodes status"), 2628 None, 0, _PerJobOp(operator.attrgetter("status"))), 2629 (_MakeField("oplog", "OpCode_log", QFT_OTHER, 2630 "List of opcode output logs"), 2631 None, 0, _PerJobOp(operator.attrgetter("log"))), 2632 (_MakeField("opstart", "OpCode_start", QFT_OTHER, 2633 "List of opcode start timestamps (before acquiring locks)"), 2634 None, 0, _PerJobOp(operator.attrgetter("start_timestamp"))), 2635 (_MakeField("opexec", "OpCode_exec", QFT_OTHER, 2636 "List of opcode execution start timestamps (after acquiring" 2637 " locks)"), 2638 None, 0, _PerJobOp(operator.attrgetter("exec_timestamp"))), 2639 (_MakeField("opend", "OpCode_end", QFT_OTHER, 2640 "List of opcode execution end timestamps"), 2641 None, 0, _PerJobOp(operator.attrgetter("end_timestamp"))), 2642 (_MakeField("oppriority", "OpCode_prio", QFT_OTHER, 2643 "List of opcode priorities"), 2644 None, 0, _PerJobOp(operator.attrgetter("priority"))), 2645 (_MakeField("summary", "Summary", QFT_OTHER, 2646 "List of per-opcode summaries"), 2647 None, 0, _PerJobOp(lambda op: op.input.Summary())), 2648 ] 2649 2650 # Timestamp fields 2651 for (name, attr, title, desc) in [ 2652 ("received_ts", "received_timestamp", "Received", 2653 "Timestamp of when job was received"), 2654 ("start_ts", "start_timestamp", "Start", "Timestamp of job start"), 2655 ("end_ts", "end_timestamp", "End", "Timestamp of job end"), 2656 ]: 2657 getter = operator.attrgetter(attr) 2658 fields.extend([ 2659 (_MakeField(name, title, QFT_OTHER, 2660 "%s (tuple containing seconds and microseconds)" % desc), 2661 None, QFF_SPLIT_TIMESTAMP, _JobTimestamp(getter)), 2662 ]) 2663 2664 return _PrepareFieldList(fields, [])
2665 2666
2667 -def _GetExportName(_, (node_name, expname)): # pylint: disable=W0613
2668 """Returns an export name if available. 2669 2670 """ 2671 if expname is None: 2672 return _FS_NODATA 2673 else: 2674 return expname 2675 2676
2677 -def _BuildExportFields():
2678 """Builds list of fields for exports. 2679 2680 """ 2681 fields = [ 2682 (_MakeField("node", "Node", QFT_TEXT, "Node name"), 2683 None, QFF_HOSTNAME, lambda _, (node_name, expname): node_name), 2684 (_MakeField("export", "Export", QFT_TEXT, "Export name"), 2685 None, 0, _GetExportName), 2686 ] 2687 2688 return _PrepareFieldList(fields, [])
2689 2690 2691 _CLUSTER_VERSION_FIELDS = { 2692 "software_version": ("SoftwareVersion", QFT_TEXT, constants.RELEASE_VERSION, 2693 "Software version"), 2694 "protocol_version": ("ProtocolVersion", QFT_NUMBER, 2695 constants.PROTOCOL_VERSION, 2696 "RPC protocol version"), 2697 "config_version": ("ConfigVersion", QFT_NUMBER, constants.CONFIG_VERSION, 2698 "Configuration format version"), 2699 "os_api_version": ("OsApiVersion", QFT_NUMBER, max(constants.OS_API_VERSIONS), 2700 "API version for OS template scripts"), 2701 "export_version": ("ExportVersion", QFT_NUMBER, constants.EXPORT_VERSION, 2702 "Import/export file format version"), 2703 "vcs_version": ("VCSVersion", QFT_TEXT, constants.VCS_VERSION, 2704 "VCS version"), 2705 } 2706 2707 2708 _CLUSTER_SIMPLE_FIELDS = { 2709 "cluster_name": ("Name", QFT_TEXT, QFF_HOSTNAME, "Cluster name"), 2710 "volume_group_name": ("VgName", QFT_TEXT, 0, "LVM volume group name"), 2711 } 2712 2713
2714 -class ClusterQueryData(object):
2715 - def __init__(self, cluster, nodes, drain_flag, watcher_pause):
2716 """Initializes this class. 2717 2718 @type cluster: L{objects.Cluster} 2719 @param cluster: Instance of cluster object 2720 @type nodes: dict; node UUID as key 2721 @param nodes: Node objects 2722 @type drain_flag: bool 2723 @param drain_flag: Whether job queue is drained 2724 @type watcher_pause: number 2725 @param watcher_pause: Until when watcher is paused (Unix timestamp) 2726 2727 """ 2728 self._cluster = cluster 2729 self.nodes = nodes 2730 self.drain_flag = drain_flag 2731 self.watcher_pause = watcher_pause
2732
2733 - def __iter__(self):
2734 return iter([self._cluster])
2735 2736
2737 -def _ClusterWatcherPause(ctx, _):
2738 """Returns until when watcher is paused (if available). 2739 2740 """ 2741 if ctx.watcher_pause is None: 2742 return _FS_UNAVAIL 2743 else: 2744 return ctx.watcher_pause
2745 2746
2747 -def _BuildClusterFields():
2748 """Builds list of fields for cluster information. 2749 2750 """ 2751 fields = [ 2752 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), CQ_CONFIG, 0, 2753 lambda ctx, cluster: list(cluster.GetTags())), 2754 (_MakeField("architecture", "ArchInfo", QFT_OTHER, 2755 "Architecture information"), None, 0, 2756 lambda ctx, _: runtime.GetArchInfo()), 2757 (_MakeField("drain_flag", "QueueDrained", QFT_BOOL, 2758 "Flag whether job queue is drained"), CQ_QUEUE_DRAINED, 0, 2759 lambda ctx, _: ctx.drain_flag), 2760 (_MakeField("watcher_pause", "WatcherPause", QFT_TIMESTAMP, 2761 "Until when watcher is paused"), CQ_WATCHER_PAUSE, 0, 2762 _ClusterWatcherPause), 2763 (_MakeField("master_node", "Master", QFT_TEXT, "Master node name"), 2764 CQ_CONFIG, QFF_HOSTNAME, 2765 lambda ctx, cluster: _GetNodeName(ctx, None, cluster.master_node)), 2766 (_MakeField("hv_state", "HypervisorState", QFT_OTHER, 2767 "Custom static hypervisor state"), 2768 CQ_CONFIG, 0, _GetItemAttr("hv_state_static")), 2769 (_MakeField("disk_state", "DiskState", QFT_OTHER, "Disk state"), 2770 CQ_CONFIG, 0, _GetItemAttr("disk_state_static")), 2771 ] 2772 2773 # Simple fields 2774 fields.extend([ 2775 (_MakeField(name, title, kind, doc), CQ_CONFIG, flags, _GetItemAttr(name)) 2776 for (name, (title, kind, flags, doc)) in _CLUSTER_SIMPLE_FIELDS.items() 2777 ],) 2778 2779 # Version fields 2780 fields.extend([ 2781 (_MakeField(name, title, kind, doc), None, 0, _StaticValue(value)) 2782 for (name, (title, kind, value, doc)) in _CLUSTER_VERSION_FIELDS.items()]) 2783 2784 # Add timestamps 2785 fields.extend(_GetItemTimestampFields(CQ_CONFIG)) 2786 2787 return _PrepareFieldList(fields, [ 2788 ("name", "cluster_name")])
2789 2790
2791 -class NetworkQueryData(object):
2792 """Data container for network data queries. 2793 2794 """
2795 - def __init__(self, networks, network_to_groups, 2796 network_to_instances, stats):
2797 """Initializes this class. 2798 2799 @param networks: List of network objects 2800 @type network_to_groups: dict; network UUID as key 2801 @param network_to_groups: Per-network list of groups 2802 @type network_to_instances: dict; network UUID as key 2803 @param network_to_instances: Per-network list of instances 2804 @type stats: dict; network UUID as key 2805 @param stats: Per-network usage statistics 2806 2807 """ 2808 self.networks = networks 2809 self.network_to_groups = network_to_groups 2810 self.network_to_instances = network_to_instances 2811 self.stats = stats
2812
2813 - def __iter__(self):
2814 """Iterate over all networks. 2815 2816 """ 2817 for net in self.networks: 2818 if self.stats: 2819 self.curstats = self.stats.get(net.uuid, None) 2820 else: 2821 self.curstats = None 2822 yield net
2823 2824 2825 _NETWORK_SIMPLE_FIELDS = { 2826 "name": ("Network", QFT_TEXT, 0, "Name"), 2827 "network": ("Subnet", QFT_TEXT, 0, "IPv4 subnet"), 2828 "gateway": ("Gateway", QFT_OTHER, 0, "IPv4 gateway"), 2829 "network6": ("IPv6Subnet", QFT_OTHER, 0, "IPv6 subnet"), 2830 "gateway6": ("IPv6Gateway", QFT_OTHER, 0, "IPv6 gateway"), 2831 "mac_prefix": ("MacPrefix", QFT_OTHER, 0, "MAC address prefix"), 2832 "serial_no": ("SerialNo", QFT_NUMBER, 0, _SERIAL_NO_DOC % "Network"), 2833 "uuid": ("UUID", QFT_TEXT, 0, "Network UUID"), 2834 } 2835 2836 2837 _NETWORK_STATS_FIELDS = { 2838 "free_count": ("FreeCount", QFT_NUMBER, 0, "Number of available addresses"), 2839 "reserved_count": 2840 ("ReservedCount", QFT_NUMBER, 0, "Number of reserved addresses"), 2841 "map": ("Map", QFT_TEXT, 0, "Actual mapping"), 2842 "external_reservations": 2843 ("ExternalReservations", QFT_TEXT, 0, "External reservations"), 2844 } 2845 2846
2847 -def _GetNetworkStatsField(field, kind, ctx, _):
2848 """Gets the value of a "stats" field from L{NetworkQueryData}. 2849 2850 @param field: Field name 2851 @param kind: Data kind, one of L{constants.QFT_ALL} 2852 @type ctx: L{NetworkQueryData} 2853 2854 """ 2855 return _GetStatsField(field, kind, ctx.curstats)
2856 2857
2858 -def _BuildNetworkFields():
2859 """Builds list of fields for network queries. 2860 2861 """ 2862 fields = [ 2863 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), IQ_CONFIG, 0, 2864 lambda ctx, inst: list(inst.GetTags())), 2865 ] 2866 2867 # Add simple fields 2868 fields.extend([ 2869 (_MakeField(name, title, kind, doc), 2870 NETQ_CONFIG, 0, _GetItemMaybeAttr(name)) 2871 for (name, (title, kind, _, doc)) in _NETWORK_SIMPLE_FIELDS.items()]) 2872 2873 def _GetLength(getter): 2874 return lambda ctx, network: len(getter(ctx)[network.uuid])
2875 2876 def _GetSortedList(getter): 2877 return lambda ctx, network: utils.NiceSort(getter(ctx)[network.uuid]) 2878 2879 network_to_groups = operator.attrgetter("network_to_groups") 2880 network_to_instances = operator.attrgetter("network_to_instances") 2881 2882 # Add fields for node groups 2883 fields.extend([ 2884 (_MakeField("group_cnt", "NodeGroups", QFT_NUMBER, "Number of nodegroups"), 2885 NETQ_GROUP, 0, _GetLength(network_to_groups)), 2886 (_MakeField("group_list", "GroupList", QFT_OTHER, 2887 "List of nodegroups (group name, NIC mode, NIC link)"), 2888 NETQ_GROUP, 0, lambda ctx, network: network_to_groups(ctx)[network.uuid]), 2889 ]) 2890 2891 # Add fields for instances 2892 fields.extend([ 2893 (_MakeField("inst_cnt", "Instances", QFT_NUMBER, "Number of instances"), 2894 NETQ_INST, 0, _GetLength(network_to_instances)), 2895 (_MakeField("inst_list", "InstanceList", QFT_OTHER, "List of instances"), 2896 NETQ_INST, 0, _GetSortedList(network_to_instances)), 2897 ]) 2898 2899 # Add fields for usage statistics 2900 fields.extend([ 2901 (_MakeField(name, title, kind, doc), NETQ_STATS, 0, 2902 compat.partial(_GetNetworkStatsField, name, kind)) 2903 for (name, (title, kind, _, doc)) in _NETWORK_STATS_FIELDS.items()]) 2904 2905 # Add timestamps 2906 fields.extend(_GetItemTimestampFields(IQ_NETWORKS)) 2907 2908 return _PrepareFieldList(fields, []) 2909 2910 2911 _FILTER_SIMPLE_FIELDS = { 2912 "watermark": ("Watermark", QFT_NUMBER, 0, "Watermark"), 2913 "priority": ("Priority", QFT_NUMBER, 0, "Priority"), 2914 "predicates": ("Predicates", QFT_OTHER, 0, "Predicates"), 2915 "action": ("Action", QFT_OTHER, 0, "Action"), 2916 "reason_trail": ("ReasonTrail", QFT_OTHER, 0, "Reason trail"), 2917 "uuid": ("UUID", QFT_TEXT, 0, "Network UUID"), 2918 } 2919 2920
2921 -def _BuildFilterFields():
2922 """Builds list of fields for job filter queries. 2923 2924 """ 2925 fields = [ 2926 (_MakeField(name, title, kind, doc), None, 0, _GetItemMaybeAttr(name)) 2927 for (name, (title, kind, _, doc)) in _FILTER_SIMPLE_FIELDS.items() 2928 ] 2929 2930 return _PrepareFieldList(fields, [])
2931 2932 #: Fields for cluster information 2933 CLUSTER_FIELDS = _BuildClusterFields() 2934 2935 #: Fields available for node queries 2936 NODE_FIELDS = _BuildNodeFields() 2937 2938 #: Fields available for instance queries 2939 INSTANCE_FIELDS = _BuildInstanceFields() 2940 2941 #: Fields available for lock queries 2942 LOCK_FIELDS = _BuildLockFields() 2943 2944 #: Fields available for node group queries 2945 GROUP_FIELDS = _BuildGroupFields() 2946 2947 #: Fields available for operating system queries 2948 OS_FIELDS = _BuildOsFields() 2949 2950 #: Fields available for extstorage provider queries 2951 EXTSTORAGE_FIELDS = _BuildExtStorageFields() 2952 2953 #: Fields available for job queries 2954 JOB_FIELDS = _BuildJobFields() 2955 2956 #: Fields available for exports 2957 EXPORT_FIELDS = _BuildExportFields() 2958 2959 #: Fields available for network queries 2960 NETWORK_FIELDS = _BuildNetworkFields() 2961 2962 #: Fields available for job filter queries 2963 FILTER_FIELDS = _BuildFilterFields() 2964 2965 #: All available resources 2966 ALL_FIELDS = { 2967 constants.QR_CLUSTER: CLUSTER_FIELDS, 2968 constants.QR_INSTANCE: INSTANCE_FIELDS, 2969 constants.QR_NODE: NODE_FIELDS, 2970 constants.QR_LOCK: LOCK_FIELDS, 2971 constants.QR_GROUP: GROUP_FIELDS, 2972 constants.QR_OS: OS_FIELDS, 2973 constants.QR_EXTSTORAGE: EXTSTORAGE_FIELDS, 2974 constants.QR_JOB: JOB_FIELDS, 2975 constants.QR_EXPORT: EXPORT_FIELDS, 2976 constants.QR_NETWORK: NETWORK_FIELDS, 2977 constants.QR_FILTER: FILTER_FIELDS, 2978 } 2979 2980 #: All available field lists 2981 ALL_FIELD_LISTS = ALL_FIELDS.values() 2982