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