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

Source Code for Module ganeti.query

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