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  # 
   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_uuid):
950 """Determine node role. 951 952 @type node: L{objects.Node} 953 @param node: Node object 954 @type master_uuid: string 955 @param master_uuid: Master node UUID 956 957 """ 958 if node.uuid == master_uuid: 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 _GetItemMaybeAttr(attr):
981 """Returns a field function to return a not-None attribute of the item. 982 983 If the value is None, then C{_FS_UNAVAIL} will be returned instead. 984 985 @param attr: Attribute name 986 987 """ 988 def _helper(_, obj): 989 val = getattr(obj, attr) 990 if val is None: 991 return _FS_UNAVAIL 992 else: 993 return val
994 return _helper 995 996
997 -def _GetNDParam(name):
998 """Return a field function to return an ND parameter out of the context. 999 1000 """ 1001 def _helper(ctx, _): 1002 if ctx.ndparams is None: 1003 return _FS_UNAVAIL 1004 else: 1005 return ctx.ndparams.get(name, None)
1006 return _helper 1007 1008
1009 -def _BuildNDFields(is_group):
1010 """Builds all the ndparam fields. 1011 1012 @param is_group: whether this is called at group or node level 1013 1014 """ 1015 if is_group: 1016 field_kind = GQ_CONFIG 1017 else: 1018 field_kind = NQ_GROUP 1019 return [(_MakeField("ndp/%s" % name, 1020 constants.NDS_PARAMETER_TITLES.get(name, 1021 "ndp/%s" % name), 1022 _VTToQFT[kind], "The \"%s\" node parameter" % name), 1023 field_kind, 0, _GetNDParam(name)) 1024 for name, kind in constants.NDS_PARAMETER_TYPES.items()]
1025 1026
1027 -def _ConvWrapInner(convert, fn, ctx, item):
1028 """Wrapper for converting values. 1029 1030 @param convert: Conversion function receiving value as single parameter 1031 @param fn: Retrieval function 1032 1033 """ 1034 value = fn(ctx, item) 1035 1036 # Is the value an abnormal status? 1037 if compat.any(value is fs for fs in _FS_ALL): 1038 # Return right away 1039 return value 1040 1041 # TODO: Should conversion function also receive context, item or both? 1042 return convert(value)
1043 1044
1045 -def _ConvWrap(convert, fn):
1046 """Convenience wrapper for L{_ConvWrapInner}. 1047 1048 @param convert: Conversion function receiving value as single parameter 1049 @param fn: Retrieval function 1050 1051 """ 1052 return compat.partial(_ConvWrapInner, convert, fn)
1053 1054
1055 -def _GetItemTimestamp(getter):
1056 """Returns function for getting timestamp of item. 1057 1058 @type getter: callable 1059 @param getter: Function to retrieve timestamp attribute 1060 1061 """ 1062 def fn(_, item): 1063 """Returns a timestamp of item. 1064 1065 """ 1066 timestamp = getter(item) 1067 if timestamp is None: 1068 # Old configs might not have all timestamps 1069 return _FS_UNAVAIL 1070 else: 1071 return timestamp
1072 1073 return fn 1074 1075
1076 -def _GetItemTimestampFields(datatype):
1077 """Returns common timestamp fields. 1078 1079 @param datatype: Field data type for use by L{Query.RequestedData} 1080 1081 """ 1082 return [ 1083 (_MakeField("ctime", "CTime", QFT_TIMESTAMP, "Creation timestamp"), 1084 datatype, 0, _GetItemTimestamp(operator.attrgetter("ctime"))), 1085 (_MakeField("mtime", "MTime", QFT_TIMESTAMP, "Modification timestamp"), 1086 datatype, 0, _GetItemTimestamp(operator.attrgetter("mtime"))), 1087 ]
1088 1089
1090 -class NodeQueryData:
1091 """Data container for node data queries. 1092 1093 """
1094 - def __init__(self, nodes, live_data, master_uuid, node_to_primary, 1095 node_to_secondary, inst_uuid_to_inst_name, groups, oob_support, 1096 cluster):
1097 """Initializes this class. 1098 1099 """ 1100 self.nodes = nodes 1101 self.live_data = live_data 1102 self.master_uuid = master_uuid 1103 self.node_to_primary = node_to_primary 1104 self.node_to_secondary = node_to_secondary 1105 self.inst_uuid_to_inst_name = inst_uuid_to_inst_name 1106 self.groups = groups 1107 self.oob_support = oob_support 1108 self.cluster = cluster 1109 1110 # Used for individual rows 1111 self.curlive_data = None 1112 self.ndparams = None
1113
1114 - def __iter__(self):
1115 """Iterate over all nodes. 1116 1117 This function has side-effects and only one instance of the resulting 1118 generator should be used at a time. 1119 1120 """ 1121 for node in self.nodes: 1122 group = self.groups.get(node.group, None) 1123 if group is None: 1124 self.ndparams = None 1125 else: 1126 self.ndparams = self.cluster.FillND(node, group) 1127 if self.live_data: 1128 self.curlive_data = self.live_data.get(node.uuid, None) 1129 else: 1130 self.curlive_data = None 1131 yield node
1132 1133 1134 #: Fields that are direct attributes of an L{objects.Node} object 1135 _NODE_SIMPLE_FIELDS = { 1136 "drained": ("Drained", QFT_BOOL, 0, "Whether node is drained"), 1137 "master_candidate": ("MasterC", QFT_BOOL, 0, 1138 "Whether node is a master candidate"), 1139 "master_capable": ("MasterCapable", QFT_BOOL, 0, 1140 "Whether node can become a master candidate"), 1141 "name": ("Node", QFT_TEXT, QFF_HOSTNAME, "Node name"), 1142 "offline": ("Offline", QFT_BOOL, 0, "Whether node is marked offline"), 1143 "serial_no": ("SerialNo", QFT_NUMBER, 0, _SERIAL_NO_DOC % "Node"), 1144 "uuid": ("UUID", QFT_TEXT, 0, "Node UUID"), 1145 "vm_capable": ("VMCapable", QFT_BOOL, 0, "Whether node can host instances"), 1146 } 1147 1148 1149 #: Fields requiring talking to the node 1150 # Note that none of these are available for non-vm_capable nodes 1151 _NODE_LIVE_FIELDS = { 1152 "bootid": ("BootID", QFT_TEXT, "bootid", 1153 "Random UUID renewed for each system reboot, can be used" 1154 " for detecting reboots by tracking changes"), 1155 "cnodes": ("CNodes", QFT_NUMBER, "cpu_nodes", 1156 "Number of NUMA domains on node (if exported by hypervisor)"), 1157 "cnos": ("CNOs", QFT_NUMBER, "cpu_dom0", 1158 "Number of logical processors used by the node OS (dom0 for Xen)"), 1159 "csockets": ("CSockets", QFT_NUMBER, "cpu_sockets", 1160 "Number of physical CPU sockets (if exported by hypervisor)"), 1161 "ctotal": ("CTotal", QFT_NUMBER, "cpu_total", "Number of logical processors"), 1162 "dfree": ("DFree", QFT_UNIT, "storage_free", 1163 "Available storage space in storage unit"), 1164 "dtotal": ("DTotal", QFT_UNIT, "storage_size", 1165 "Total storage space in storage unit used for instance disk" 1166 " allocation"), 1167 "spfree": ("SpFree", QFT_NUMBER, "spindles_free", 1168 "Available spindles in volume group (exclusive storage only)"), 1169 "sptotal": ("SpTotal", QFT_NUMBER, "spindles_total", 1170 "Total spindles in volume group (exclusive storage only)"), 1171 "mfree": ("MFree", QFT_UNIT, "memory_free", 1172 "Memory available for instance allocations"), 1173 "mnode": ("MNode", QFT_UNIT, "memory_dom0", 1174 "Amount of memory used by node (dom0 for Xen)"), 1175 "mtotal": ("MTotal", QFT_UNIT, "memory_total", 1176 "Total amount of memory of physical machine"), 1177 } 1178 1179
1180 -def _GetGroup(cb):
1181 """Build function for calling another function with an node group. 1182 1183 @param cb: The callback to be called with the nodegroup 1184 1185 """ 1186 def fn(ctx, node): 1187 """Get group data for a node. 1188 1189 @type ctx: L{NodeQueryData} 1190 @type inst: L{objects.Node} 1191 @param inst: Node object 1192 1193 """ 1194 ng = ctx.groups.get(node.group, None) 1195 if ng is None: 1196 # Nodes always have a group, or the configuration is corrupt 1197 return _FS_UNAVAIL 1198 1199 return cb(ctx, node, ng)
1200 1201 return fn 1202 1203
1204 -def _GetNodeGroup(ctx, node, ng): # pylint: disable=W0613
1205 """Returns the name of a node's group. 1206 1207 @type ctx: L{NodeQueryData} 1208 @type node: L{objects.Node} 1209 @param node: Node object 1210 @type ng: L{objects.NodeGroup} 1211 @param ng: The node group this node belongs to 1212 1213 """ 1214 return ng.name 1215 1216
1217 -def _GetNodePower(ctx, node):
1218 """Returns the node powered state 1219 1220 @type ctx: L{NodeQueryData} 1221 @type node: L{objects.Node} 1222 @param node: Node object 1223 1224 """ 1225 if ctx.oob_support[node.uuid]: 1226 return node.powered 1227 1228 return _FS_UNAVAIL
1229 1230
1231 -def _GetNdParams(ctx, node, ng):
1232 """Returns the ndparams for this node. 1233 1234 @type ctx: L{NodeQueryData} 1235 @type node: L{objects.Node} 1236 @param node: Node object 1237 @type ng: L{objects.NodeGroup} 1238 @param ng: The node group this node belongs to 1239 1240 """ 1241 return ctx.cluster.SimpleFillND(ng.FillND(node))
1242 1243
1244 -def _GetLiveNodeField(field, kind, ctx, node):
1245 """Gets the value of a "live" field from L{NodeQueryData}. 1246 1247 @param field: Live field name 1248 @param kind: Data kind, one of L{constants.QFT_ALL} 1249 @type ctx: L{NodeQueryData} 1250 @type node: L{objects.Node} 1251 @param node: Node object 1252 1253 """ 1254 if node.offline: 1255 return _FS_OFFLINE 1256 1257 if not node.vm_capable: 1258 return _FS_UNAVAIL 1259 1260 if not ctx.curlive_data: 1261 return _FS_NODATA 1262 1263 return _GetStatsField(field, kind, ctx.curlive_data)
1264 1265
1266 -def _GetStatsField(field, kind, data):
1267 """Gets a value from live statistics. 1268 1269 If the value is not found, L{_FS_UNAVAIL} is returned. If the field kind is 1270 numeric a conversion to integer is attempted. If that fails, L{_FS_UNAVAIL} 1271 is returned. 1272 1273 @param field: Live field name 1274 @param kind: Data kind, one of L{constants.QFT_ALL} 1275 @type data: dict 1276 @param data: Statistics 1277 1278 """ 1279 try: 1280 value = data[field] 1281 except KeyError: 1282 return _FS_UNAVAIL 1283 1284 if kind == QFT_TEXT: 1285 return value 1286 1287 assert kind in (QFT_NUMBER, QFT_UNIT) 1288 1289 # Try to convert into number 1290 try: 1291 return int(value) 1292 except (ValueError, TypeError): 1293 logging.exception("Failed to convert node field '%s' (value %r) to int", 1294 field, value) 1295 return _FS_UNAVAIL
1296 1297
1298 -def _GetNodeHvState(_, node):
1299 """Converts node's hypervisor state for query result. 1300 1301 """ 1302 hv_state = node.hv_state 1303 1304 if hv_state is None: 1305 return _FS_UNAVAIL 1306 1307 return dict((name, value.ToDict()) for (name, value) in hv_state.items())
1308 1309
1310 -def _GetNodeDiskState(_, node):
1311 """Converts node's disk state for query result. 1312 1313 """ 1314 disk_state = node.disk_state 1315 1316 if disk_state is None: 1317 return _FS_UNAVAIL 1318 1319 return dict((disk_kind, dict((name, value.ToDict()) 1320 for (name, value) in kind_state.items())) 1321 for (disk_kind, kind_state) in disk_state.items())
1322 1323
1324 -def _BuildNodeFields():
1325 """Builds list of fields for node queries. 1326 1327 """ 1328 fields = [ 1329 (_MakeField("pip", "PrimaryIP", QFT_TEXT, "Primary IP address"), 1330 NQ_CONFIG, 0, _GetItemAttr("primary_ip")), 1331 (_MakeField("sip", "SecondaryIP", QFT_TEXT, "Secondary IP address"), 1332 NQ_CONFIG, 0, _GetItemAttr("secondary_ip")), 1333 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), NQ_CONFIG, 0, 1334 lambda ctx, node: list(node.GetTags())), 1335 (_MakeField("master", "IsMaster", QFT_BOOL, "Whether node is master"), 1336 NQ_CONFIG, 0, lambda ctx, node: node.uuid == ctx.master_uuid), 1337 (_MakeField("group", "Group", QFT_TEXT, "Node group"), NQ_GROUP, 0, 1338 _GetGroup(_GetNodeGroup)), 1339 (_MakeField("group.uuid", "GroupUUID", QFT_TEXT, "UUID of node group"), 1340 NQ_CONFIG, 0, _GetItemAttr("group")), 1341 (_MakeField("powered", "Powered", QFT_BOOL, 1342 "Whether node is thought to be powered on"), 1343 NQ_OOB, 0, _GetNodePower), 1344 (_MakeField("ndparams", "NodeParameters", QFT_OTHER, 1345 "Merged node parameters"), 1346 NQ_GROUP, 0, _GetGroup(_GetNdParams)), 1347 (_MakeField("custom_ndparams", "CustomNodeParameters", QFT_OTHER, 1348 "Custom node parameters"), 1349 NQ_GROUP, 0, _GetItemAttr("ndparams")), 1350 (_MakeField("hv_state", "HypervisorState", QFT_OTHER, "Hypervisor state"), 1351 NQ_CONFIG, 0, _GetNodeHvState), 1352 (_MakeField("disk_state", "DiskState", QFT_OTHER, "Disk state"), 1353 NQ_CONFIG, 0, _GetNodeDiskState), 1354 ] 1355 1356 fields.extend(_BuildNDFields(False)) 1357 1358 # Node role 1359 role_values = (constants.NR_MASTER, constants.NR_MCANDIDATE, 1360 constants.NR_REGULAR, constants.NR_DRAINED, 1361 constants.NR_OFFLINE) 1362 role_doc = ("Node role; \"%s\" for master, \"%s\" for master candidate," 1363 " \"%s\" for regular, \"%s\" for drained, \"%s\" for offline" % 1364 role_values) 1365 fields.append((_MakeField("role", "Role", QFT_TEXT, role_doc), NQ_CONFIG, 0, 1366 lambda ctx, node: _GetNodeRole(node, ctx.master_uuid))) 1367 assert set(role_values) == constants.NR_ALL 1368 1369 def _GetLength(getter): 1370 return lambda ctx, node: len(getter(ctx)[node.uuid])
1371 1372 def _GetList(getter): 1373 return lambda ctx, node: utils.NiceSort( 1374 [ctx.inst_uuid_to_inst_name[uuid] 1375 for uuid in getter(ctx)[node.uuid]]) 1376 1377 # Add fields operating on instance lists 1378 for prefix, titleprefix, docword, getter in \ 1379 [("p", "Pri", "primary", operator.attrgetter("node_to_primary")), 1380 ("s", "Sec", "secondary", operator.attrgetter("node_to_secondary"))]: 1381 # TODO: Allow filterting by hostname in list 1382 fields.extend([ 1383 (_MakeField("%sinst_cnt" % prefix, "%sinst" % prefix.upper(), QFT_NUMBER, 1384 "Number of instances with this node as %s" % docword), 1385 NQ_INST, 0, _GetLength(getter)), 1386 (_MakeField("%sinst_list" % prefix, "%sInstances" % titleprefix, 1387 QFT_OTHER, 1388 "List of instances with this node as %s" % docword), 1389 NQ_INST, 0, _GetList(getter)), 1390 ]) 1391 1392 # Add simple fields 1393 fields.extend([ 1394 (_MakeField(name, title, kind, doc), NQ_CONFIG, flags, _GetItemAttr(name)) 1395 for (name, (title, kind, flags, doc)) in _NODE_SIMPLE_FIELDS.items()]) 1396 1397 # Add fields requiring live data 1398 fields.extend([ 1399 (_MakeField(name, title, kind, doc), NQ_LIVE, 0, 1400 compat.partial(_GetLiveNodeField, nfield, kind)) 1401 for (name, (title, kind, nfield, doc)) in _NODE_LIVE_FIELDS.items()]) 1402 1403 # Add timestamps 1404 fields.extend(_GetItemTimestampFields(NQ_CONFIG)) 1405 1406 return _PrepareFieldList(fields, []) 1407 1408
1409 -class InstanceQueryData:
1410 """Data container for instance data queries. 1411 1412 """
1413 - def __init__(self, instances, cluster, disk_usage, offline_node_uuids, 1414 bad_node_uuids, live_data, wrongnode_inst, console, nodes, 1415 groups, networks):
1416 """Initializes this class. 1417 1418 @param instances: List of instance objects 1419 @param cluster: Cluster object 1420 @type disk_usage: dict; instance UUID as key 1421 @param disk_usage: Per-instance disk usage 1422 @type offline_node_uuids: list of strings 1423 @param offline_node_uuids: List of offline nodes 1424 @type bad_node_uuids: list of strings 1425 @param bad_node_uuids: List of faulty nodes 1426 @type live_data: dict; instance UUID as key 1427 @param live_data: Per-instance live data 1428 @type wrongnode_inst: set 1429 @param wrongnode_inst: Set of instances running on wrong node(s) 1430 @type console: dict; instance UUID as key 1431 @param console: Per-instance console information 1432 @type nodes: dict; node UUID as key 1433 @param nodes: Node objects 1434 @type networks: dict; net_uuid as key 1435 @param networks: Network objects 1436 1437 """ 1438 assert len(set(bad_node_uuids) & set(offline_node_uuids)) == \ 1439 len(offline_node_uuids), \ 1440 "Offline nodes not included in bad nodes" 1441 assert not (set(live_data.keys()) & set(bad_node_uuids)), \ 1442 "Found live data for bad or offline nodes" 1443 1444 self.instances = instances 1445 self.cluster = cluster 1446 self.disk_usage = disk_usage 1447 self.offline_nodes = offline_node_uuids 1448 self.bad_nodes = bad_node_uuids 1449 self.live_data = live_data 1450 self.wrongnode_inst = wrongnode_inst 1451 self.console = console 1452 self.nodes = nodes 1453 self.groups = groups 1454 self.networks = networks 1455 1456 # Used for individual rows 1457 self.inst_hvparams = None 1458 self.inst_beparams = None 1459 self.inst_osparams = None 1460 self.inst_nicparams = None
1461
1462 - def __iter__(self):
1463 """Iterate over all instances. 1464 1465 This function has side-effects and only one instance of the resulting 1466 generator should be used at a time. 1467 1468 """ 1469 for inst in self.instances: 1470 self.inst_hvparams = self.cluster.FillHV(inst, skip_globals=True) 1471 self.inst_beparams = self.cluster.FillBE(inst) 1472 self.inst_osparams = self.cluster.SimpleFillOS(inst.os, inst.osparams) 1473 self.inst_nicparams = [self.cluster.SimpleFillNIC(nic.nicparams) 1474 for nic in inst.nics] 1475 1476 yield inst
1477 1478
1479 -def _GetInstOperState(ctx, inst):
1480 """Get instance's operational status. 1481 1482 @type ctx: L{InstanceQueryData} 1483 @type inst: L{objects.Instance} 1484 @param inst: Instance object 1485 1486 """ 1487 # Can't use RS_OFFLINE here as it would describe the instance to 1488 # be offline when we actually don't know due to missing data 1489 if inst.primary_node in ctx.bad_nodes: 1490 return _FS_NODATA 1491 else: 1492 return bool(ctx.live_data.get(inst.uuid))
1493 1494
1495 -def _GetInstLiveData(name):
1496 """Build function for retrieving live data. 1497 1498 @type name: string 1499 @param name: Live data field name 1500 1501 """ 1502 def fn(ctx, inst): 1503 """Get live data for an instance. 1504 1505 @type ctx: L{InstanceQueryData} 1506 @type inst: L{objects.Instance} 1507 @param inst: Instance object 1508 1509 """ 1510 if (inst.primary_node in ctx.bad_nodes or 1511 inst.primary_node in ctx.offline_nodes): 1512 # Can't use RS_OFFLINE here as it would describe the instance to be 1513 # offline when we actually don't know due to missing data 1514 return _FS_NODATA 1515 1516 if inst.uuid in ctx.live_data: 1517 data = ctx.live_data[inst.uuid] 1518 if name in data: 1519 return data[name] 1520 1521 return _FS_UNAVAIL
1522 1523 return fn 1524 1525
1526 -def _GetInstStatus(ctx, inst):
1527 """Get instance status. 1528 1529 @type ctx: L{InstanceQueryData} 1530 @type inst: L{objects.Instance} 1531 @param inst: Instance object 1532 1533 """ 1534 if inst.primary_node in ctx.offline_nodes: 1535 return constants.INSTST_NODEOFFLINE 1536 1537 if inst.primary_node in ctx.bad_nodes: 1538 return constants.INSTST_NODEDOWN 1539 1540 if bool(ctx.live_data.get(inst.uuid)): 1541 if inst.uuid in ctx.wrongnode_inst: 1542 return constants.INSTST_WRONGNODE 1543 elif inst.admin_state == constants.ADMINST_UP: 1544 return constants.INSTST_RUNNING 1545 else: 1546 return constants.INSTST_ERRORUP 1547 1548 if inst.admin_state == constants.ADMINST_UP: 1549 return constants.INSTST_ERRORDOWN 1550 elif inst.admin_state == constants.ADMINST_DOWN: 1551 return constants.INSTST_ADMINDOWN 1552 1553 return constants.INSTST_ADMINOFFLINE
1554 1555
1556 -def _GetInstDisk(index, cb):
1557 """Build function for calling another function with an instance Disk. 1558 1559 @type index: int 1560 @param index: Disk index 1561 @type cb: callable 1562 @param cb: Callback 1563 1564 """ 1565 def fn(ctx, inst): 1566 """Call helper function with instance Disk. 1567 1568 @type ctx: L{InstanceQueryData} 1569 @type inst: L{objects.Instance} 1570 @param inst: Instance object 1571 1572 """ 1573 try: 1574 nic = inst.disks[index] 1575 except IndexError: 1576 return _FS_UNAVAIL 1577 1578 return cb(ctx, index, nic)
1579 1580 return fn 1581 1582
1583 -def _GetInstDiskSize(ctx, _, disk): # pylint: disable=W0613
1584 """Get a Disk's size. 1585 1586 @type ctx: L{InstanceQueryData} 1587 @type disk: L{objects.Disk} 1588 @param disk: The Disk object 1589 1590 """ 1591 if disk.size is None: 1592 return _FS_UNAVAIL 1593 else: 1594 return disk.size 1595 1596
1597 -def _GetInstDiskSpindles(ctx, _, disk): # pylint: disable=W0613
1598 """Get a Disk's spindles. 1599 1600 @type disk: L{objects.Disk} 1601 @param disk: The Disk object 1602 1603 """ 1604 if disk.spindles is None: 1605 return _FS_UNAVAIL 1606 else: 1607 return disk.spindles 1608 1609
1610 -def _GetInstDeviceName(ctx, _, device): # pylint: disable=W0613
1611 """Get a Device's Name. 1612 1613 @type ctx: L{InstanceQueryData} 1614 @type device: L{objects.NIC} or L{objects.Disk} 1615 @param device: The NIC or Disk object 1616 1617 """ 1618 if device.name is None: 1619 return _FS_UNAVAIL 1620 else: 1621 return device.name 1622 1623
1624 -def _GetInstDeviceUUID(ctx, _, device): # pylint: disable=W0613
1625 """Get a Device's UUID. 1626 1627 @type ctx: L{InstanceQueryData} 1628 @type device: L{objects.NIC} or L{objects.Disk} 1629 @param device: The NIC or Disk object 1630 1631 """ 1632 if device.uuid is None: 1633 return _FS_UNAVAIL 1634 else: 1635 return device.uuid 1636 1637
1638 -def _GetInstNic(index, cb):
1639 """Build function for calling another function with an instance NIC. 1640 1641 @type index: int 1642 @param index: NIC index 1643 @type cb: callable 1644 @param cb: Callback 1645 1646 """ 1647 def fn(ctx, inst): 1648 """Call helper function with instance NIC. 1649 1650 @type ctx: L{InstanceQueryData} 1651 @type inst: L{objects.Instance} 1652 @param inst: Instance object 1653 1654 """ 1655 try: 1656 nic = inst.nics[index] 1657 except IndexError: 1658 return _FS_UNAVAIL 1659 1660 return cb(ctx, index, nic)
1661 1662 return fn 1663 1664
1665 -def _GetInstNicNetworkName(ctx, _, nic): # pylint: disable=W0613
1666 """Get a NIC's Network. 1667 1668 @type ctx: L{InstanceQueryData} 1669 @type nic: L{objects.NIC} 1670 @param nic: NIC object 1671 1672 """ 1673 if nic.network is None: 1674 return _FS_UNAVAIL 1675 else: 1676 return ctx.networks[nic.network].name 1677 1678
1679 -def _GetInstNicNetwork(ctx, _, nic): # pylint: disable=W0613
1680 """Get a NIC's Network. 1681 1682 @type ctx: L{InstanceQueryData} 1683 @type nic: L{objects.NIC} 1684 @param nic: NIC object 1685 1686 """ 1687 if nic.network is None: 1688 return _FS_UNAVAIL 1689 else: 1690 return nic.network 1691 1692
1693 -def _GetInstNicIp(ctx, _, nic): # pylint: disable=W0613
1694 """Get a NIC's IP address. 1695 1696 @type ctx: L{InstanceQueryData} 1697 @type nic: L{objects.NIC} 1698 @param nic: NIC object 1699 1700 """ 1701 if nic.ip is None: 1702 return _FS_UNAVAIL 1703 else: 1704 return nic.ip 1705 1706
1707 -def _GetInstNicBridge(ctx, index, _):
1708 """Get a NIC's bridge. 1709 1710 @type ctx: L{InstanceQueryData} 1711 @type index: int 1712 @param index: NIC index 1713 1714 """ 1715 assert len(ctx.inst_nicparams) >= index 1716 1717 nicparams = ctx.inst_nicparams[index] 1718 1719 if nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED: 1720 return nicparams[constants.NIC_LINK] 1721 else: 1722 return _FS_UNAVAIL
1723 1724
1725 -def _GetInstAllNicNetworkNames(ctx, inst):
1726 """Get all network names for an instance. 1727 1728 @type ctx: L{InstanceQueryData} 1729 @type inst: L{objects.Instance} 1730 @param inst: Instance object 1731 1732 """ 1733 result = [] 1734 1735 for nic in inst.nics: 1736 name = None 1737 if nic.network: 1738 name = ctx.networks[nic.network].name 1739 result.append(name) 1740 1741 assert len(result) == len(inst.nics) 1742 1743 return result
1744 1745
1746 -def _GetInstAllNicBridges(ctx, inst):
1747 """Get all network bridges for an instance. 1748 1749 @type ctx: L{InstanceQueryData} 1750 @type inst: L{objects.Instance} 1751 @param inst: Instance object 1752 1753 """ 1754 assert len(ctx.inst_nicparams) == len(inst.nics) 1755 1756 result = [] 1757 1758 for nicp in ctx.inst_nicparams: 1759 if nicp[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED: 1760 result.append(nicp[constants.NIC_LINK]) 1761 else: 1762 result.append(None) 1763 1764 assert len(result) == len(inst.nics) 1765 1766 return result
1767 1768
1769 -def _GetInstNicParam(name):
1770 """Build function for retrieving a NIC parameter. 1771 1772 @type name: string 1773 @param name: Parameter name 1774 1775 """ 1776 def fn(ctx, index, _): 1777 """Get a NIC's bridge. 1778 1779 @type ctx: L{InstanceQueryData} 1780 @type inst: L{objects.Instance} 1781 @param inst: Instance object 1782 @type nic: L{objects.NIC} 1783 @param nic: NIC object 1784 1785 """ 1786 assert len(ctx.inst_nicparams) >= index 1787 return ctx.inst_nicparams[index][name]
1788 1789 return fn 1790 1791
1792 -def _GetInstanceNetworkFields():
1793 """Get instance fields involving network interfaces. 1794 1795 @return: Tuple containing list of field definitions used as input for 1796 L{_PrepareFieldList} and a list of aliases 1797 1798 """ 1799 nic_mac_fn = lambda ctx, _, nic: nic.mac 1800 nic_mode_fn = _GetInstNicParam(constants.NIC_MODE) 1801 nic_link_fn = _GetInstNicParam(constants.NIC_LINK) 1802 1803 fields = [ 1804 # All NICs 1805 (_MakeField("nic.count", "NICs", QFT_NUMBER, 1806 "Number of network interfaces"), 1807 IQ_CONFIG, 0, lambda ctx, inst: len(inst.nics)), 1808 (_MakeField("nic.macs", "NIC_MACs", QFT_OTHER, 1809 "List containing each network interface's MAC address"), 1810 IQ_CONFIG, 0, lambda ctx, inst: [nic.mac for nic in inst.nics]), 1811 (_MakeField("nic.ips", "NIC_IPs", QFT_OTHER, 1812 "List containing each network interface's IP address"), 1813 IQ_CONFIG, 0, lambda ctx, inst: [nic.ip for nic in inst.nics]), 1814 (_MakeField("nic.names", "NIC_Names", QFT_OTHER, 1815 "List containing each network interface's name"), 1816 IQ_CONFIG, 0, lambda ctx, inst: [nic.name for nic in inst.nics]), 1817 (_MakeField("nic.uuids", "NIC_UUIDs", QFT_OTHER, 1818 "List containing each network interface's UUID"), 1819 IQ_CONFIG, 0, lambda ctx, inst: [nic.uuid for nic in inst.nics]), 1820 (_MakeField("nic.modes", "NIC_modes", QFT_OTHER, 1821 "List containing each network interface's mode"), IQ_CONFIG, 0, 1822 lambda ctx, inst: [nicp[constants.NIC_MODE] 1823 for nicp in ctx.inst_nicparams]), 1824 (_MakeField("nic.links", "NIC_links", QFT_OTHER, 1825 "List containing each network interface's link"), IQ_CONFIG, 0, 1826 lambda ctx, inst: [nicp[constants.NIC_LINK] 1827 for nicp in ctx.inst_nicparams]), 1828 (_MakeField("nic.bridges", "NIC_bridges", QFT_OTHER, 1829 "List containing each network interface's bridge"), 1830 IQ_CONFIG, 0, _GetInstAllNicBridges), 1831 (_MakeField("nic.networks", "NIC_networks", QFT_OTHER, 1832 "List containing each interface's network"), IQ_CONFIG, 0, 1833 lambda ctx, inst: [nic.network for nic in inst.nics]), 1834 (_MakeField("nic.networks.names", "NIC_networks_names", QFT_OTHER, 1835 "List containing each interface's network"), 1836 IQ_NETWORKS, 0, _GetInstAllNicNetworkNames) 1837 ] 1838 1839 # NICs by number 1840 for i in range(constants.MAX_NICS): 1841 numtext = utils.FormatOrdinal(i + 1) 1842 fields.extend([ 1843 (_MakeField("nic.ip/%s" % i, "NicIP/%s" % i, QFT_TEXT, 1844 "IP address of %s network interface" % numtext), 1845 IQ_CONFIG, 0, _GetInstNic(i, _GetInstNicIp)), 1846 (_MakeField("nic.mac/%s" % i, "NicMAC/%s" % i, QFT_TEXT, 1847 "MAC address of %s network interface" % numtext), 1848 IQ_CONFIG, 0, _GetInstNic(i, nic_mac_fn)), 1849 (_MakeField("nic.name/%s" % i, "NicName/%s" % i, QFT_TEXT, 1850 "Name address of %s network interface" % numtext), 1851 IQ_CONFIG, 0, _GetInstNic(i, _GetInstDeviceName)), 1852 (_MakeField("nic.uuid/%s" % i, "NicUUID/%s" % i, QFT_TEXT, 1853 "UUID address of %s network interface" % numtext), 1854 IQ_CONFIG, 0, _GetInstNic(i, _GetInstDeviceUUID)), 1855 (_MakeField("nic.mode/%s" % i, "NicMode/%s" % i, QFT_TEXT, 1856 "Mode of %s network interface" % numtext), 1857 IQ_CONFIG, 0, _GetInstNic(i, nic_mode_fn)), 1858 (_MakeField("nic.link/%s" % i, "NicLink/%s" % i, QFT_TEXT, 1859 "Link of %s network interface" % numtext), 1860 IQ_CONFIG, 0, _GetInstNic(i, nic_link_fn)), 1861 (_MakeField("nic.bridge/%s" % i, "NicBridge/%s" % i, QFT_TEXT, 1862 "Bridge of %s network interface" % numtext), 1863 IQ_CONFIG, 0, _GetInstNic(i, _GetInstNicBridge)), 1864 (_MakeField("nic.network/%s" % i, "NicNetwork/%s" % i, QFT_TEXT, 1865 "Network of %s network interface" % numtext), 1866 IQ_CONFIG, 0, _GetInstNic(i, _GetInstNicNetwork)), 1867 (_MakeField("nic.network.name/%s" % i, "NicNetworkName/%s" % i, QFT_TEXT, 1868 "Network name of %s network interface" % numtext), 1869 IQ_NETWORKS, 0, _GetInstNic(i, _GetInstNicNetworkName)), 1870 ]) 1871 1872 aliases = [ 1873 # Legacy fields for first NIC 1874 ("ip", "nic.ip/0"), 1875 ("mac", "nic.mac/0"), 1876 ("bridge", "nic.bridge/0"), 1877 ("nic_mode", "nic.mode/0"), 1878 ("nic_link", "nic.link/0"), 1879 ("nic_network", "nic.network/0"), 1880 ] 1881 1882 return (fields, aliases)
1883 1884
1885 -def _GetInstDiskUsage(ctx, inst):
1886 """Get disk usage for an instance. 1887 1888 @type ctx: L{InstanceQueryData} 1889 @type inst: L{objects.Instance} 1890 @param inst: Instance object 1891 1892 """ 1893 usage = ctx.disk_usage[inst.uuid] 1894 1895 if usage is None: 1896 usage = 0 1897 1898 return usage
1899 1900
1901 -def _GetInstanceConsole(ctx, inst):
1902 """Get console information for instance. 1903 1904 @type ctx: L{InstanceQueryData} 1905 @type inst: L{objects.Instance} 1906 @param inst: Instance object 1907 1908 """ 1909 consinfo = ctx.console[inst.uuid] 1910 1911 if consinfo is None: 1912 return _FS_UNAVAIL 1913 1914 return consinfo
1915 1916
1917 -def _GetInstanceDiskFields():
1918 """Get instance fields involving disks. 1919 1920 @return: List of field definitions used as input for L{_PrepareFieldList} 1921 1922 """ 1923 fields = [ 1924 (_MakeField("disk_usage", "DiskUsage", QFT_UNIT, 1925 "Total disk space used by instance on each of its nodes;" 1926 " this is not the disk size visible to the instance, but" 1927 " the usage on the node"), 1928 IQ_DISKUSAGE, 0, _GetInstDiskUsage), 1929 (_MakeField("disk.count", "Disks", QFT_NUMBER, "Number of disks"), 1930 IQ_CONFIG, 0, lambda ctx, inst: len(inst.disks)), 1931 (_MakeField("disk.sizes", "Disk_sizes", QFT_OTHER, "List of disk sizes"), 1932 IQ_CONFIG, 0, lambda ctx, inst: [disk.size for disk in inst.disks]), 1933 (_MakeField("disk.spindles", "Disk_spindles", QFT_OTHER, 1934 "List of disk spindles"), 1935 IQ_CONFIG, 0, lambda ctx, inst: [disk.spindles for disk in inst.disks]), 1936 (_MakeField("disk.names", "Disk_names", QFT_OTHER, "List of disk names"), 1937 IQ_CONFIG, 0, lambda ctx, inst: [disk.name for disk in inst.disks]), 1938 (_MakeField("disk.uuids", "Disk_UUIDs", QFT_OTHER, "List of disk UUIDs"), 1939 IQ_CONFIG, 0, lambda ctx, inst: [disk.uuid for disk in inst.disks]), 1940 ] 1941 1942 # Disks by number 1943 for i in range(constants.MAX_DISKS): 1944 numtext = utils.FormatOrdinal(i + 1) 1945 fields.extend([ 1946 (_MakeField("disk.size/%s" % i, "Disk/%s" % i, QFT_UNIT, 1947 "Disk size of %s disk" % numtext), 1948 IQ_CONFIG, 0, _GetInstDisk(i, _GetInstDiskSize)), 1949 (_MakeField("disk.spindles/%s" % i, "DiskSpindles/%s" % i, QFT_NUMBER, 1950 "Spindles of %s disk" % numtext), 1951 IQ_CONFIG, 0, _GetInstDisk(i, _GetInstDiskSpindles)), 1952 (_MakeField("disk.name/%s" % i, "DiskName/%s" % i, QFT_TEXT, 1953 "Name of %s disk" % numtext), 1954 IQ_CONFIG, 0, _GetInstDisk(i, _GetInstDeviceName)), 1955 (_MakeField("disk.uuid/%s" % i, "DiskUUID/%s" % i, QFT_TEXT, 1956 "UUID of %s disk" % numtext), 1957 IQ_CONFIG, 0, _GetInstDisk(i, _GetInstDeviceUUID))]) 1958 1959 return fields
1960 1961
1962 -def _GetInstanceParameterFields():
1963 """Get instance fields involving parameters. 1964 1965 @return: List of field definitions used as input for L{_PrepareFieldList} 1966 1967 """ 1968 fields = [ 1969 # Filled parameters 1970 (_MakeField("hvparams", "HypervisorParameters", QFT_OTHER, 1971 "Hypervisor parameters (merged)"), 1972 IQ_CONFIG, 0, lambda ctx, _: ctx.inst_hvparams), 1973 (_MakeField("beparams", "BackendParameters", QFT_OTHER, 1974 "Backend parameters (merged)"), 1975 IQ_CONFIG, 0, lambda ctx, _: ctx.inst_beparams), 1976 (_MakeField("osparams", "OpSysParameters", QFT_OTHER, 1977 "Operating system parameters (merged)"), 1978 IQ_CONFIG, 0, lambda ctx, _: ctx.inst_osparams), 1979 1980 # Unfilled parameters 1981 (_MakeField("custom_hvparams", "CustomHypervisorParameters", QFT_OTHER, 1982 "Custom hypervisor parameters"), 1983 IQ_CONFIG, 0, _GetItemAttr("hvparams")), 1984 (_MakeField("custom_beparams", "CustomBackendParameters", QFT_OTHER, 1985 "Custom backend parameters",), 1986 IQ_CONFIG, 0, _GetItemAttr("beparams")), 1987 (_MakeField("custom_osparams", "CustomOpSysParameters", QFT_OTHER, 1988 "Custom operating system parameters",), 1989 IQ_CONFIG, 0, _GetItemAttr("osparams")), 1990 (_MakeField("custom_nicparams", "CustomNicParameters", QFT_OTHER, 1991 "Custom network interface parameters"), 1992 IQ_CONFIG, 0, lambda ctx, inst: [nic.nicparams for nic in inst.nics]), 1993 ] 1994 1995 # HV params 1996 def _GetInstHvParam(name): 1997 return lambda ctx, _: ctx.inst_hvparams.get(name, _FS_UNAVAIL)
1998 1999 fields.extend([ 2000 (_MakeField("hv/%s" % name, 2001 constants.HVS_PARAMETER_TITLES.get(name, "hv/%s" % name), 2002 _VTToQFT[kind], "The \"%s\" hypervisor parameter" % name), 2003 IQ_CONFIG, 0, _GetInstHvParam(name)) 2004 for name, kind in constants.HVS_PARAMETER_TYPES.items() 2005 if name not in constants.HVC_GLOBALS]) 2006 2007 # BE params 2008 def _GetInstBeParam(name): 2009 return lambda ctx, _: ctx.inst_beparams.get(name, None) 2010 2011 fields.extend([ 2012 (_MakeField("be/%s" % name, 2013 constants.BES_PARAMETER_TITLES.get(name, "be/%s" % name), 2014 _VTToQFT[kind], "The \"%s\" backend parameter" % name), 2015 IQ_CONFIG, 0, _GetInstBeParam(name)) 2016 for name, kind in constants.BES_PARAMETER_TYPES.items()]) 2017 2018 return fields 2019 2020 2021 _INST_SIMPLE_FIELDS = { 2022 "disk_template": ("Disk_template", QFT_TEXT, 0, "Instance disk template"), 2023 "hypervisor": ("Hypervisor", QFT_TEXT, 0, "Hypervisor name"), 2024 "name": ("Instance", QFT_TEXT, QFF_HOSTNAME, "Instance name"), 2025 # Depending on the hypervisor, the port can be None 2026 "network_port": ("Network_port", QFT_OTHER, 0, 2027 "Instance network port if available (e.g. for VNC console)"), 2028 "os": ("OS", QFT_TEXT, 0, "Operating system"), 2029 "serial_no": ("SerialNo", QFT_NUMBER, 0, _SERIAL_NO_DOC % "Instance"), 2030 "uuid": ("UUID", QFT_TEXT, 0, "Instance UUID"), 2031 } 2032 2033
2034 -def _GetNodeName(ctx, default, node_uuid):
2035 """Gets node name of a node. 2036 2037 @type ctx: L{InstanceQueryData} 2038 @param default: Default value 2039 @type node_uuid: string 2040 @param node_uuid: Node UUID 2041 2042 """ 2043 try: 2044 node = ctx.nodes[node_uuid] 2045 except KeyError: 2046 return default 2047 else: 2048 return node.name
2049 2050
2051 -def _GetInstNodeGroup(ctx, default, node_uuid):
2052 """Gets group UUID of an instance node. 2053 2054 @type ctx: L{InstanceQueryData} 2055 @param default: Default value 2056 @type node_uuid: string 2057 @param node_uuid: Node UUID 2058 2059 """ 2060 try: 2061 node = ctx.nodes[node_uuid] 2062 except KeyError: 2063 return default 2064 else: 2065 return node.group
2066 2067
2068 -def _GetInstNodeGroupName(ctx, default, node_uuid):
2069 """Gets group name of an instance node. 2070 2071 @type ctx: L{InstanceQueryData} 2072 @param default: Default value 2073 @type node_uuid: string 2074 @param node_uuid: Node UUID 2075 2076 """ 2077 try: 2078 node = ctx.nodes[node_uuid] 2079 except KeyError: 2080 return default 2081 2082 try: 2083 group = ctx.groups[node.group] 2084 except KeyError: 2085 return default 2086 2087 return group.name
2088 2089
2090 -def _BuildInstanceFields():
2091 """Builds list of fields for instance queries. 2092 2093 """ 2094 fields = [ 2095 (_MakeField("pnode", "Primary_node", QFT_TEXT, "Primary node"), 2096 IQ_NODES, QFF_HOSTNAME, 2097 lambda ctx, inst: _GetNodeName(ctx, None, inst.primary_node)), 2098 (_MakeField("pnode.group", "PrimaryNodeGroup", QFT_TEXT, 2099 "Primary node's group"), 2100 IQ_NODES, 0, 2101 lambda ctx, inst: _GetInstNodeGroupName(ctx, _FS_UNAVAIL, 2102 inst.primary_node)), 2103 (_MakeField("pnode.group.uuid", "PrimaryNodeGroupUUID", QFT_TEXT, 2104 "Primary node's group UUID"), 2105 IQ_NODES, 0, 2106 lambda ctx, inst: _GetInstNodeGroup(ctx, _FS_UNAVAIL, inst.primary_node)), 2107 # TODO: Allow filtering by secondary node as hostname 2108 (_MakeField("snodes", "Secondary_Nodes", QFT_OTHER, 2109 "Secondary nodes; usually this will just be one node"), 2110 IQ_NODES, 0, 2111 lambda ctx, inst: map(compat.partial(_GetNodeName, ctx, None), 2112 inst.secondary_nodes)), 2113 (_MakeField("snodes.group", "SecondaryNodesGroups", QFT_OTHER, 2114 "Node groups of secondary nodes"), 2115 IQ_NODES, 0, 2116 lambda ctx, inst: map(compat.partial(_GetInstNodeGroupName, ctx, None), 2117 inst.secondary_nodes)), 2118 (_MakeField("snodes.group.uuid", "SecondaryNodesGroupsUUID", QFT_OTHER, 2119 "Node group UUIDs of secondary nodes"), 2120 IQ_NODES, 0, 2121 lambda ctx, inst: map(compat.partial(_GetInstNodeGroup, ctx, None), 2122 inst.secondary_nodes)), 2123 (_MakeField("admin_state", "InstanceState", QFT_TEXT, 2124 "Desired state of instance"), 2125 IQ_CONFIG, 0, _GetItemAttr("admin_state")), 2126 (_MakeField("admin_up", "Autostart", QFT_BOOL, 2127 "Desired state of instance"), 2128 IQ_CONFIG, 0, lambda ctx, inst: inst.admin_state == constants.ADMINST_UP), 2129 (_MakeField("disks_active", "DisksActive", QFT_BOOL, 2130 "Desired state of instance disks"), 2131 IQ_CONFIG, 0, _GetItemAttr("disks_active")), 2132 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), IQ_CONFIG, 0, 2133 lambda ctx, inst: list(inst.GetTags())), 2134 (_MakeField("console", "Console", QFT_OTHER, 2135 "Instance console information"), IQ_CONSOLE, 0, 2136 _GetInstanceConsole), 2137 ] 2138 2139 # Add simple fields 2140 fields.extend([ 2141 (_MakeField(name, title, kind, doc), IQ_CONFIG, flags, _GetItemAttr(name)) 2142 for (name, (title, kind, flags, doc)) in _INST_SIMPLE_FIELDS.items()]) 2143 2144 # Fields requiring talking to the node 2145 fields.extend([ 2146 (_MakeField("oper_state", "Running", QFT_BOOL, "Actual state of instance"), 2147 IQ_LIVE, 0, _GetInstOperState), 2148 (_MakeField("oper_ram", "Memory", QFT_UNIT, 2149 "Actual memory usage as seen by hypervisor"), 2150 IQ_LIVE, 0, _GetInstLiveData("memory")), 2151 (_MakeField("oper_vcpus", "VCPUs", QFT_NUMBER, 2152 "Actual number of VCPUs as seen by hypervisor"), 2153 IQ_LIVE, 0, _GetInstLiveData("vcpus")), 2154 ]) 2155 2156 # Status field 2157 status_values = (constants.INSTST_RUNNING, constants.INSTST_ADMINDOWN, 2158 constants.INSTST_WRONGNODE, constants.INSTST_ERRORUP, 2159 constants.INSTST_ERRORDOWN, constants.INSTST_NODEDOWN, 2160 constants.INSTST_NODEOFFLINE, constants.INSTST_ADMINOFFLINE) 2161 status_doc = ("Instance status; \"%s\" if instance is set to be running" 2162 " and actually is, \"%s\" if instance is stopped and" 2163 " is not running, \"%s\" if instance running, but not on its" 2164 " designated primary node, \"%s\" if instance should be" 2165 " stopped, but is actually running, \"%s\" if instance should" 2166 " run, but doesn't, \"%s\" if instance's primary node is down," 2167 " \"%s\" if instance's primary node is marked offline," 2168 " \"%s\" if instance is offline and does not use dynamic" 2169 " resources" % status_values) 2170 fields.append((_MakeField("status", "Status", QFT_TEXT, status_doc), 2171 IQ_LIVE, 0, _GetInstStatus)) 2172 assert set(status_values) == constants.INSTST_ALL, \ 2173 "Status documentation mismatch" 2174 2175 (network_fields, network_aliases) = _GetInstanceNetworkFields() 2176 2177 fields.extend(network_fields) 2178 fields.extend(_GetInstanceParameterFields()) 2179 fields.extend(_GetInstanceDiskFields()) 2180 fields.extend(_GetItemTimestampFields(IQ_CONFIG)) 2181 2182 aliases = [ 2183 ("vcpus", "be/vcpus"), 2184 ("be/memory", "be/maxmem"), 2185 ("sda_size", "disk.size/0"), 2186 ("sdb_size", "disk.size/1"), 2187 ] + network_aliases 2188 2189 return _PrepareFieldList(fields, aliases)
2190 2191
2192 -class LockQueryData:
2193 """Data container for lock data queries. 2194 2195 """
2196 - def __init__(self, lockdata):
2197 """Initializes this class. 2198 2199 """ 2200 self.lockdata = lockdata
2201
2202 - def __iter__(self):
2203 """Iterate over all locks. 2204 2205 """ 2206 return iter(self.lockdata)
2207 2208
2209 -def _GetLockOwners(_, data):
2210 """Returns a sorted list of a lock's current owners. 2211 2212 """ 2213 (_, _, owners, _) = data 2214 2215 if owners: 2216 owners = utils.NiceSort(owners) 2217 2218 return owners
2219 2220
2221 -def _GetLockPending(_, data):
2222 """Returns a sorted list of a lock's pending acquires. 2223 2224 """ 2225 (_, _, _, pending) = data 2226 2227 if pending: 2228 pending = [(mode, utils.NiceSort(names)) 2229 for (mode, names) in pending] 2230 2231 return pending
2232 2233
2234 -def _BuildLockFields():
2235 """Builds list of fields for lock queries. 2236 2237 """ 2238 return _PrepareFieldList([ 2239 # TODO: Lock names are not always hostnames. Should QFF_HOSTNAME be used? 2240 (_MakeField("name", "Name", QFT_TEXT, "Lock name"), None, 0, 2241 lambda ctx, (name, mode, owners, pending): name), 2242 (_MakeField("mode", "Mode", QFT_OTHER, 2243 "Mode in which the lock is currently acquired" 2244 " (exclusive or shared)"), 2245 LQ_MODE, 0, lambda ctx, (name, mode, owners, pending): mode), 2246 (_MakeField("owner", "Owner", QFT_OTHER, "Current lock owner(s)"), 2247 LQ_OWNER, 0, _GetLockOwners), 2248 (_MakeField("pending", "Pending", QFT_OTHER, 2249 "Threads waiting for the lock"), 2250 LQ_PENDING, 0, _GetLockPending), 2251 ], [])
2252 2253
2254 -class GroupQueryData:
2255 """Data container for node group data queries. 2256 2257 """
2258 - def __init__(self, cluster, groups, group_to_nodes, group_to_instances, 2259 want_diskparams):
2260 """Initializes this class. 2261 2262 @param cluster: Cluster object 2263 @param groups: List of node group objects 2264 @type group_to_nodes: dict; group UUID as key 2265 @param group_to_nodes: Per-group list of nodes 2266 @type group_to_instances: dict; group UUID as key 2267 @param group_to_instances: Per-group list of (primary) instances 2268 @type want_diskparams: bool 2269 @param want_diskparams: Whether diskparamters should be calculated 2270 2271 """ 2272 self.groups = groups 2273 self.group_to_nodes = group_to_nodes 2274 self.group_to_instances = group_to_instances 2275 self.cluster = cluster 2276 self.want_diskparams = want_diskparams 2277 2278 # Used for individual rows 2279 self.group_ipolicy = None 2280 self.ndparams = None 2281 self.group_dp = None
2282
2283 - def __iter__(self):
2284 """Iterate over all node groups. 2285 2286 This function has side-effects and only one instance of the resulting 2287 generator should be used at a time. 2288 2289 """ 2290 for group in self.groups: 2291 self.group_ipolicy = self.cluster.SimpleFillIPolicy(group.ipolicy) 2292 self.ndparams = self.cluster.SimpleFillND(group.ndparams) 2293 if self.want_diskparams: 2294 self.group_dp = self.cluster.SimpleFillDP(group.diskparams) 2295 else: 2296 self.group_dp = None 2297 yield group
2298 2299 2300 _GROUP_SIMPLE_FIELDS = { 2301 "alloc_policy": ("AllocPolicy", QFT_TEXT, "Allocation policy for group"), 2302 "name": ("Group", QFT_TEXT, "Group name"), 2303 "serial_no": ("SerialNo", QFT_NUMBER, _SERIAL_NO_DOC % "Group"), 2304 "uuid": ("UUID", QFT_TEXT, "Group UUID"), 2305 } 2306 2307
2308 -def _BuildGroupFields():
2309 """Builds list of fields for node group queries. 2310 2311 """ 2312 # Add simple fields 2313 fields = [(_MakeField(name, title, kind, doc), GQ_CONFIG, 0, 2314 _GetItemAttr(name)) 2315 for (name, (title, kind, doc)) in _GROUP_SIMPLE_FIELDS.items()] 2316 2317 def _GetLength(getter): 2318 return lambda ctx, group: len(getter(ctx)[group.uuid])
2319 2320 def _GetSortedList(getter): 2321 return lambda ctx, group: utils.NiceSort(getter(ctx)[group.uuid]) 2322 2323 group_to_nodes = operator.attrgetter("group_to_nodes") 2324 group_to_instances = operator.attrgetter("group_to_instances") 2325 2326 # Add fields for nodes 2327 fields.extend([ 2328 (_MakeField("node_cnt", "Nodes", QFT_NUMBER, "Number of nodes"), 2329 GQ_NODE, 0, _GetLength(group_to_nodes)), 2330 (_MakeField("node_list", "NodeList", QFT_OTHER, "List of nodes"), 2331 GQ_NODE, 0, _GetSortedList(group_to_nodes)), 2332 ]) 2333 2334 # Add fields for instances 2335 fields.extend([ 2336 (_MakeField("pinst_cnt", "Instances", QFT_NUMBER, 2337 "Number of primary instances"), 2338 GQ_INST, 0, _GetLength(group_to_instances)), 2339 (_MakeField("pinst_list", "InstanceList", QFT_OTHER, 2340 "List of primary instances"), 2341 GQ_INST, 0, _GetSortedList(group_to_instances)), 2342 ]) 2343 2344 # Other fields 2345 fields.extend([ 2346 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), GQ_CONFIG, 0, 2347 lambda ctx, group: list(group.GetTags())), 2348 (_MakeField("ipolicy", "InstancePolicy", QFT_OTHER, 2349 "Instance policy limitations (merged)"), 2350 GQ_CONFIG, 0, lambda ctx, _: ctx.group_ipolicy), 2351 (_MakeField("custom_ipolicy", "CustomInstancePolicy", QFT_OTHER, 2352 "Custom instance policy limitations"), 2353 GQ_CONFIG, 0, _GetItemAttr("ipolicy")), 2354 (_MakeField("custom_ndparams", "CustomNDParams", QFT_OTHER, 2355 "Custom node parameters"), 2356 GQ_CONFIG, 0, _GetItemAttr("ndparams")), 2357 (_MakeField("ndparams", "NDParams", QFT_OTHER, 2358 "Node parameters"), 2359 GQ_CONFIG, 0, lambda ctx, _: ctx.ndparams), 2360 (_MakeField("diskparams", "DiskParameters", QFT_OTHER, 2361 "Disk parameters (merged)"), 2362 GQ_DISKPARAMS, 0, lambda ctx, _: ctx.group_dp), 2363 (_MakeField("custom_diskparams", "CustomDiskParameters", QFT_OTHER, 2364 "Custom disk parameters"), 2365 GQ_CONFIG, 0, _GetItemAttr("diskparams")), 2366 ]) 2367 2368 # ND parameters 2369 fields.extend(_BuildNDFields(True)) 2370 2371 fields.extend(_GetItemTimestampFields(GQ_CONFIG)) 2372 2373 return _PrepareFieldList(fields, []) 2374 2375
2376 -class OsInfo(objects.ConfigObject):
2377 __slots__ = [ 2378 "name", 2379 "valid", 2380 "hidden", 2381 "blacklisted", 2382 "variants", 2383 "api_versions", 2384 "parameters", 2385 "node_status", 2386 ]
2387 2388
2389 -def _BuildOsFields():
2390 """Builds list of fields for operating system queries. 2391 2392 """ 2393 fields = [ 2394 (_MakeField("name", "Name", QFT_TEXT, "Operating system name"), 2395 None, 0, _GetItemAttr("name")), 2396 (_MakeField("valid", "Valid", QFT_BOOL, 2397 "Whether operating system definition is valid"), 2398 None, 0, _GetItemAttr("valid")), 2399 (_MakeField("hidden", "Hidden", QFT_BOOL, 2400 "Whether operating system is hidden"), 2401 None, 0, _GetItemAttr("hidden")), 2402 (_MakeField("blacklisted", "Blacklisted", QFT_BOOL, 2403 "Whether operating system is blacklisted"), 2404 None, 0, _GetItemAttr("blacklisted")), 2405 (_MakeField("variants", "Variants", QFT_OTHER, 2406 "Operating system variants"), 2407 None, 0, _ConvWrap(utils.NiceSort, _GetItemAttr("variants"))), 2408 (_MakeField("api_versions", "ApiVersions", QFT_OTHER, 2409 "Operating system API versions"), 2410 None, 0, _ConvWrap(sorted, _GetItemAttr("api_versions"))), 2411 (_MakeField("parameters", "Parameters", QFT_OTHER, 2412 "Operating system parameters"), 2413 None, 0, _ConvWrap(compat.partial(utils.NiceSort, key=compat.fst), 2414 _GetItemAttr("parameters"))), 2415 (_MakeField("node_status", "NodeStatus", QFT_OTHER, 2416 "Status from node"), 2417 None, 0, _GetItemAttr("node_status")), 2418 ] 2419 2420 return _PrepareFieldList(fields, [])
2421 2422
2423 -class ExtStorageInfo(objects.ConfigObject):
2424 __slots__ = [ 2425 "name", 2426 "node_status", 2427 "nodegroup_status", 2428 "parameters", 2429 ]
2430 2431
2432 -def _BuildExtStorageFields():
2433 """Builds list of fields for extstorage provider queries. 2434 2435 """ 2436 fields = [ 2437 (_MakeField("name", "Name", QFT_TEXT, "ExtStorage provider name"), 2438 None, 0, _GetItemAttr("name")), 2439 (_MakeField("node_status", "NodeStatus", QFT_OTHER, 2440 "Status from node"), 2441 None, 0, _GetItemAttr("node_status")), 2442 (_MakeField("nodegroup_status", "NodegroupStatus", QFT_OTHER, 2443 "Overall Nodegroup status"), 2444 None, 0, _GetItemAttr("nodegroup_status")), 2445 (_MakeField("parameters", "Parameters", QFT_OTHER, 2446 "ExtStorage provider parameters"), 2447 None, 0, _GetItemAttr("parameters")), 2448 ] 2449 2450 return _PrepareFieldList(fields, [])
2451 2452
2453 -def _JobUnavailInner(fn, ctx, (job_id, job)): # pylint: disable=W0613
2454 """Return L{_FS_UNAVAIL} if job is None. 2455 2456 When listing specifc jobs (e.g. "gnt-job list 1 2 3"), a job may not be 2457 found, in which case this function converts it to L{_FS_UNAVAIL}. 2458 2459 """ 2460 if job is None: 2461 return _FS_UNAVAIL 2462 else: 2463 return fn(job) 2464 2465
2466 -def _JobUnavail(inner):
2467 """Wrapper for L{_JobUnavailInner}. 2468 2469 """ 2470 return compat.partial(_JobUnavailInner, inner)
2471 2472
2473 -def _PerJobOpInner(fn, job):
2474 """Executes a function per opcode in a job. 2475 2476 """ 2477 return map(fn, job.ops)
2478 2479
2480 -def _PerJobOp(fn):
2481 """Wrapper for L{_PerJobOpInner}. 2482 2483 """ 2484 return _JobUnavail(compat.partial(_PerJobOpInner, fn))
2485 2486
2487 -def _JobTimestampInner(fn, job):
2488 """Converts unavailable timestamp to L{_FS_UNAVAIL}. 2489 2490 """ 2491 timestamp = fn(job) 2492 2493 if timestamp is None: 2494 return _FS_UNAVAIL 2495 else: 2496 return timestamp
2497 2498
2499 -def _JobTimestamp(fn):
2500 """Wrapper for L{_JobTimestampInner}. 2501 2502 """ 2503 return _JobUnavail(compat.partial(_JobTimestampInner, fn))
2504 2505
2506 -def _BuildJobFields():
2507 """Builds list of fields for job queries. 2508 2509 """ 2510 fields = [ 2511 (_MakeField("id", "ID", QFT_NUMBER, "Job ID"), 2512 None, QFF_JOB_ID, lambda _, (job_id, job): job_id), 2513 (_MakeField("status", "Status", QFT_TEXT, "Job status"), 2514 None, 0, _JobUnavail(lambda job: job.CalcStatus())), 2515 (_MakeField("priority", "Priority", QFT_NUMBER, 2516 ("Current job priority (%s to %s)" % 2517 (constants.OP_PRIO_LOWEST, constants.OP_PRIO_HIGHEST))), 2518 None, 0, _JobUnavail(lambda job: job.CalcPriority())), 2519 (_MakeField("archived", "Archived", QFT_BOOL, "Whether job is archived"), 2520 JQ_ARCHIVED, 0, lambda _, (job_id, job): job.archived), 2521 (_MakeField("ops", "OpCodes", QFT_OTHER, "List of all opcodes"), 2522 None, 0, _PerJobOp(lambda op: op.input.__getstate__())), 2523 (_MakeField("opresult", "OpCode_result", QFT_OTHER, 2524 "List of opcodes results"), 2525 None, 0, _PerJobOp(operator.attrgetter("result"))), 2526 (_MakeField("opstatus", "OpCode_status", QFT_OTHER, 2527 "List of opcodes status"), 2528 None, 0, _PerJobOp(operator.attrgetter("status"))), 2529 (_MakeField("oplog", "OpCode_log", QFT_OTHER, 2530 "List of opcode output logs"), 2531 None, 0, _PerJobOp(operator.attrgetter("log"))), 2532 (_MakeField("opstart", "OpCode_start", QFT_OTHER, 2533 "List of opcode start timestamps (before acquiring locks)"), 2534 None, 0, _PerJobOp(operator.attrgetter("start_timestamp"))), 2535 (_MakeField("opexec", "OpCode_exec", QFT_OTHER, 2536 "List of opcode execution start timestamps (after acquiring" 2537 " locks)"), 2538 None, 0, _PerJobOp(operator.attrgetter("exec_timestamp"))), 2539 (_MakeField("opend", "OpCode_end", QFT_OTHER, 2540 "List of opcode execution end timestamps"), 2541 None, 0, _PerJobOp(operator.attrgetter("end_timestamp"))), 2542 (_MakeField("oppriority", "OpCode_prio", QFT_OTHER, 2543 "List of opcode priorities"), 2544 None, 0, _PerJobOp(operator.attrgetter("priority"))), 2545 (_MakeField("summary", "Summary", QFT_OTHER, 2546 "List of per-opcode summaries"), 2547 None, 0, _PerJobOp(lambda op: op.input.Summary())), 2548 ] 2549 2550 # Timestamp fields 2551 for (name, attr, title, desc) in [ 2552 ("received_ts", "received_timestamp", "Received", 2553 "Timestamp of when job was received"), 2554 ("start_ts", "start_timestamp", "Start", "Timestamp of job start"), 2555 ("end_ts", "end_timestamp", "End", "Timestamp of job end"), 2556 ]: 2557 getter = operator.attrgetter(attr) 2558 fields.extend([ 2559 (_MakeField(name, title, QFT_OTHER, 2560 "%s (tuple containing seconds and microseconds)" % desc), 2561 None, QFF_SPLIT_TIMESTAMP, _JobTimestamp(getter)), 2562 ]) 2563 2564 return _PrepareFieldList(fields, [])
2565 2566
2567 -def _GetExportName(_, (node_name, expname)): # pylint: disable=W0613
2568 """Returns an export name if available. 2569 2570 """ 2571 if expname is None: 2572 return _FS_NODATA 2573 else: 2574 return expname 2575 2576
2577 -def _BuildExportFields():
2578 """Builds list of fields for exports. 2579 2580 """ 2581 fields = [ 2582 (_MakeField("node", "Node", QFT_TEXT, "Node name"), 2583 None, QFF_HOSTNAME, lambda _, (node_name, expname): node_name), 2584 (_MakeField("export", "Export", QFT_TEXT, "Export name"), 2585 None, 0, _GetExportName), 2586 ] 2587 2588 return _PrepareFieldList(fields, [])
2589 2590 2591 _CLUSTER_VERSION_FIELDS = { 2592 "software_version": ("SoftwareVersion", QFT_TEXT, constants.RELEASE_VERSION, 2593 "Software version"), 2594 "protocol_version": ("ProtocolVersion", QFT_NUMBER, 2595 constants.PROTOCOL_VERSION, 2596 "RPC protocol version"), 2597 "config_version": ("ConfigVersion", QFT_NUMBER, constants.CONFIG_VERSION, 2598 "Configuration format version"), 2599 "os_api_version": ("OsApiVersion", QFT_NUMBER, max(constants.OS_API_VERSIONS), 2600 "API version for OS template scripts"), 2601 "export_version": ("ExportVersion", QFT_NUMBER, constants.EXPORT_VERSION, 2602 "Import/export file format version"), 2603 "vcs_version": ("VCSVersion", QFT_TEXT, constants.VCS_VERSION, 2604 "VCS version"), 2605 } 2606 2607 2608 _CLUSTER_SIMPLE_FIELDS = { 2609 "cluster_name": ("Name", QFT_TEXT, QFF_HOSTNAME, "Cluster name"), 2610 "volume_group_name": ("VgName", QFT_TEXT, 0, "LVM volume group name"), 2611 } 2612 2613
2614 -class ClusterQueryData:
2615 - def __init__(self, cluster, nodes, drain_flag, watcher_pause):
2616 """Initializes this class. 2617 2618 @type cluster: L{objects.Cluster} 2619 @param cluster: Instance of cluster object 2620 @type nodes: dict; node UUID as key 2621 @param nodes: Node objects 2622 @type drain_flag: bool 2623 @param drain_flag: Whether job queue is drained 2624 @type watcher_pause: number 2625 @param watcher_pause: Until when watcher is paused (Unix timestamp) 2626 2627 """ 2628 self._cluster = cluster 2629 self.nodes = nodes 2630 self.drain_flag = drain_flag 2631 self.watcher_pause = watcher_pause
2632
2633 - def __iter__(self):
2634 return iter([self._cluster])
2635 2636
2637 -def _ClusterWatcherPause(ctx, _):
2638 """Returns until when watcher is paused (if available). 2639 2640 """ 2641 if ctx.watcher_pause is None: 2642 return _FS_UNAVAIL 2643 else: 2644 return ctx.watcher_pause
2645 2646
2647 -def _BuildClusterFields():
2648 """Builds list of fields for cluster information. 2649 2650 """ 2651 fields = [ 2652 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), CQ_CONFIG, 0, 2653 lambda ctx, cluster: list(cluster.GetTags())), 2654 (_MakeField("architecture", "ArchInfo", QFT_OTHER, 2655 "Architecture information"), None, 0, 2656 lambda ctx, _: runtime.GetArchInfo()), 2657 (_MakeField("drain_flag", "QueueDrained", QFT_BOOL, 2658 "Flag whether job queue is drained"), CQ_QUEUE_DRAINED, 0, 2659 lambda ctx, _: ctx.drain_flag), 2660 (_MakeField("watcher_pause", "WatcherPause", QFT_TIMESTAMP, 2661 "Until when watcher is paused"), CQ_WATCHER_PAUSE, 0, 2662 _ClusterWatcherPause), 2663 (_MakeField("master_node", "Master", QFT_TEXT, "Master node name"), 2664 CQ_CONFIG, QFF_HOSTNAME, 2665 lambda ctx, cluster: _GetNodeName(ctx, None, cluster.master_node)), 2666 ] 2667 2668 # Simple fields 2669 fields.extend([ 2670 (_MakeField(name, title, kind, doc), CQ_CONFIG, flags, _GetItemAttr(name)) 2671 for (name, (title, kind, flags, doc)) in _CLUSTER_SIMPLE_FIELDS.items() 2672 ],) 2673 2674 # Version fields 2675 fields.extend([ 2676 (_MakeField(name, title, kind, doc), None, 0, _StaticValue(value)) 2677 for (name, (title, kind, value, doc)) in _CLUSTER_VERSION_FIELDS.items()]) 2678 2679 # Add timestamps 2680 fields.extend(_GetItemTimestampFields(CQ_CONFIG)) 2681 2682 return _PrepareFieldList(fields, [ 2683 ("name", "cluster_name")])
2684 2685
2686 -class NetworkQueryData:
2687 """Data container for network data queries. 2688 2689 """
2690 - def __init__(self, networks, network_to_groups, 2691 network_to_instances, stats):
2692 """Initializes this class. 2693 2694 @param networks: List of network objects 2695 @type network_to_groups: dict; network UUID as key 2696 @param network_to_groups: Per-network list of groups 2697 @type network_to_instances: dict; network UUID as key 2698 @param network_to_instances: Per-network list of instances 2699 @type stats: dict; network UUID as key 2700 @param stats: Per-network usage statistics 2701 2702 """ 2703 self.networks = networks 2704 self.network_to_groups = network_to_groups 2705 self.network_to_instances = network_to_instances 2706 self.stats = stats
2707
2708 - def __iter__(self):
2709 """Iterate over all networks. 2710 2711 """ 2712 for net in self.networks: 2713 if self.stats: 2714 self.curstats = self.stats.get(net.uuid, None) 2715 else: 2716 self.curstats = None 2717 yield net
2718 2719 2720 _NETWORK_SIMPLE_FIELDS = { 2721 "name": ("Network", QFT_TEXT, 0, "Name"), 2722 "network": ("Subnet", QFT_TEXT, 0, "IPv4 subnet"), 2723 "gateway": ("Gateway", QFT_OTHER, 0, "IPv4 gateway"), 2724 "network6": ("IPv6Subnet", QFT_OTHER, 0, "IPv6 subnet"), 2725 "gateway6": ("IPv6Gateway", QFT_OTHER, 0, "IPv6 gateway"), 2726 "mac_prefix": ("MacPrefix", QFT_OTHER, 0, "MAC address prefix"), 2727 "serial_no": ("SerialNo", QFT_NUMBER, 0, _SERIAL_NO_DOC % "Network"), 2728 "uuid": ("UUID", QFT_TEXT, 0, "Network UUID"), 2729 } 2730 2731 2732 _NETWORK_STATS_FIELDS = { 2733 "free_count": ("FreeCount", QFT_NUMBER, 0, "Number of available addresses"), 2734 "reserved_count": 2735 ("ReservedCount", QFT_NUMBER, 0, "Number of reserved addresses"), 2736 "map": ("Map", QFT_TEXT, 0, "Actual mapping"), 2737 "external_reservations": 2738 ("ExternalReservations", QFT_TEXT, 0, "External reservations"), 2739 } 2740 2741
2742 -def _GetNetworkStatsField(field, kind, ctx, _):
2743 """Gets the value of a "stats" field from L{NetworkQueryData}. 2744 2745 @param field: Field name 2746 @param kind: Data kind, one of L{constants.QFT_ALL} 2747 @type ctx: L{NetworkQueryData} 2748 2749 """ 2750 return _GetStatsField(field, kind, ctx.curstats)
2751 2752
2753 -def _BuildNetworkFields():
2754 """Builds list of fields for network queries. 2755 2756 """ 2757 fields = [ 2758 (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), IQ_CONFIG, 0, 2759 lambda ctx, inst: list(inst.GetTags())), 2760 ] 2761 2762 # Add simple fields 2763 fields.extend([ 2764 (_MakeField(name, title, kind, doc), 2765 NETQ_CONFIG, 0, _GetItemMaybeAttr(name)) 2766 for (name, (title, kind, _, doc)) in _NETWORK_SIMPLE_FIELDS.items()]) 2767 2768 def _GetLength(getter): 2769 return lambda ctx, network: len(getter(ctx)[network.uuid])
2770 2771 def _GetSortedList(getter): 2772 return lambda ctx, network: utils.NiceSort(getter(ctx)[network.uuid]) 2773 2774 network_to_groups = operator.attrgetter("network_to_groups") 2775 network_to_instances = operator.attrgetter("network_to_instances") 2776 2777 # Add fields for node groups 2778 fields.extend([ 2779 (_MakeField("group_cnt", "NodeGroups", QFT_NUMBER, "Number of nodegroups"), 2780 NETQ_GROUP, 0, _GetLength(network_to_groups)), 2781 (_MakeField("group_list", "GroupList", QFT_OTHER, 2782 "List of nodegroups (group name, NIC mode, NIC link)"), 2783 NETQ_GROUP, 0, lambda ctx, network: network_to_groups(ctx)[network.uuid]), 2784 ]) 2785 2786 # Add fields for instances 2787 fields.extend([ 2788 (_MakeField("inst_cnt", "Instances", QFT_NUMBER, "Number of instances"), 2789 NETQ_INST, 0, _GetLength(network_to_instances)), 2790 (_MakeField("inst_list", "InstanceList", QFT_OTHER, "List of instances"), 2791 NETQ_INST, 0, _GetSortedList(network_to_instances)), 2792 ]) 2793 2794 # Add fields for usage statistics 2795 fields.extend([ 2796 (_MakeField(name, title, kind, doc), NETQ_STATS, 0, 2797 compat.partial(_GetNetworkStatsField, name, kind)) 2798 for (name, (title, kind, _, doc)) in _NETWORK_STATS_FIELDS.items()]) 2799 2800 # Add timestamps 2801 fields.extend(_GetItemTimestampFields(IQ_NETWORKS)) 2802 2803 return _PrepareFieldList(fields, []) 2804 2805 #: Fields for cluster information 2806 CLUSTER_FIELDS = _BuildClusterFields() 2807 2808 #: Fields available for node queries 2809 NODE_FIELDS = _BuildNodeFields() 2810 2811 #: Fields available for instance queries 2812 INSTANCE_FIELDS = _BuildInstanceFields() 2813 2814 #: Fields available for lock queries 2815 LOCK_FIELDS = _BuildLockFields() 2816 2817 #: Fields available for node group queries 2818 GROUP_FIELDS = _BuildGroupFields() 2819 2820 #: Fields available for operating system queries 2821 OS_FIELDS = _BuildOsFields() 2822 2823 #: Fields available for extstorage provider queries 2824 EXTSTORAGE_FIELDS = _BuildExtStorageFields() 2825 2826 #: Fields available for job queries 2827 JOB_FIELDS = _BuildJobFields() 2828 2829 #: Fields available for exports 2830 EXPORT_FIELDS = _BuildExportFields() 2831 2832 #: Fields available for network queries 2833 NETWORK_FIELDS = _BuildNetworkFields() 2834 2835 #: All available resources 2836 ALL_FIELDS = { 2837 constants.QR_CLUSTER: CLUSTER_FIELDS, 2838 constants.QR_INSTANCE: INSTANCE_FIELDS, 2839 constants.QR_NODE: NODE_FIELDS, 2840 constants.QR_LOCK: LOCK_FIELDS, 2841 constants.QR_GROUP: GROUP_FIELDS, 2842 constants.QR_OS: OS_FIELDS, 2843 constants.QR_EXTSTORAGE: EXTSTORAGE_FIELDS, 2844 constants.QR_JOB: JOB_FIELDS, 2845 constants.QR_EXPORT: EXPORT_FIELDS, 2846 constants.QR_NETWORK: NETWORK_FIELDS, 2847 } 2848 2849 #: All available field lists 2850 ALL_FIELD_LISTS = ALL_FIELDS.values() 2851