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