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

Source Code for Module ganeti.query

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