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

Source Code for Module ganeti.query

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