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