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

Source Code for Module ganeti.query

   1  # 
   2  # 
   3   
   4  # Copyright (C) 2010, 2011 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        - Data request type, see e.g. C{NQ_*} 
  36        - A retrieval function, see L{Query.__init__} for description 
  37      - Pass list of fields through L{_PrepareFieldList} for preparation and 
  38        checks 
  39    - Instantiate L{Query} with prepared field list definition and selected fields 
  40    - Call L{Query.RequestedData} to determine what data to collect/compute 
  41    - Call L{Query.Query} or L{Query.OldStyleQuery} with collected data and use 
  42      result 
  43        - Data container must support iteration using C{__iter__} 
  44        - Items are passed to retrieval functions and can have any format 
  45    - Call L{Query.GetFields} to get list of definitions for selected fields 
  46   
  47  @attention: Retrieval functions must be idempotent. They can be called multiple 
  48    times, in any order and any number of times. This is important to keep in 
  49    mind for implementing filters in the future. 
  50   
  51  """ 
  52   
  53  import logging 
  54  import operator 
  55  import re 
  56   
  57  from ganeti import constants 
  58  from ganeti import errors 
  59  from ganeti import utils 
  60  from ganeti import compat 
  61  from ganeti import objects 
  62  from ganeti import ht 
  63   
  64  from ganeti.constants import (QFT_UNKNOWN, QFT_TEXT, QFT_BOOL, QFT_NUMBER, 
  65                                QFT_UNIT, QFT_TIMESTAMP, QFT_OTHER, 
  66                                RS_NORMAL, RS_UNKNOWN, RS_NODATA, 
  67                                RS_UNAVAIL, RS_OFFLINE) 
  68   
  69   
  70  # Constants for requesting data from the caller/data provider. Each property 
  71  # collected/computed separately by the data provider should have its own to 
  72  # only collect the requested data and not more. 
  73   
  74  (NQ_CONFIG, 
  75   NQ_INST, 
  76   NQ_LIVE, 
  77   NQ_GROUP, 
  78   NQ_OOB) = range(1, 6) 
  79   
  80  (IQ_CONFIG, 
  81   IQ_LIVE, 
  82   IQ_DISKUSAGE, 
  83   IQ_CONSOLE) = range(100, 104) 
  84   
  85  (LQ_MODE, 
  86   LQ_OWNER, 
  87   LQ_PENDING) = range(10, 13) 
  88   
  89  (GQ_CONFIG, 
  90   GQ_NODE, 
  91   GQ_INST) = range(200, 203) 
  92   
  93   
  94  FIELD_NAME_RE = re.compile(r"^[a-z0-9/._]+$") 
  95  TITLE_RE = re.compile(r"^[^\s]+$") 
  96   
  97  #: Verification function for each field type 
  98  _VERIFY_FN = { 
  99    QFT_UNKNOWN: ht.TNone, 
 100    QFT_TEXT: ht.TString, 
 101    QFT_BOOL: ht.TBool, 
 102    QFT_NUMBER: ht.TInt, 
 103    QFT_UNIT: ht.TInt, 
 104    QFT_TIMESTAMP: ht.TNumber, 
 105    QFT_OTHER: lambda _: True, 
 106    } 
 107   
 108  # Unique objects for special field statuses 
 109  _FS_UNKNOWN = object() 
 110  _FS_NODATA = object() 
 111  _FS_UNAVAIL = object() 
 112  _FS_OFFLINE = object() 
 113   
 114  #: VType to QFT mapping 
 115  _VTToQFT = { 
 116    # TODO: fix validation of empty strings 
 117    constants.VTYPE_STRING: QFT_OTHER, # since VTYPE_STRINGs can be empty 
 118    constants.VTYPE_MAYBE_STRING: QFT_OTHER, 
 119    constants.VTYPE_BOOL: QFT_BOOL, 
 120    constants.VTYPE_SIZE: QFT_UNIT, 
 121    constants.VTYPE_INT: QFT_NUMBER, 
 122    } 
 123   
 124   
125 -def _GetUnknownField(ctx, item): # pylint: disable-msg=W0613
126 """Gets the contents of an unknown field. 127 128 """ 129 return _FS_UNKNOWN 130 131
132 -def _GetQueryFields(fielddefs, selected):
133 """Calculates the internal list of selected fields. 134 135 Unknown fields are returned as L{constants.QFT_UNKNOWN}. 136 137 @type fielddefs: dict 138 @param fielddefs: Field definitions 139 @type selected: list of strings 140 @param selected: List of selected fields 141 142 """ 143 result = [] 144 145 for name in selected: 146 try: 147 fdef = fielddefs[name] 148 except KeyError: 149 fdef = (_MakeField(name, name, QFT_UNKNOWN), None, _GetUnknownField) 150 151 assert len(fdef) == 3 152 153 result.append(fdef) 154 155 return result
156 157
158 -def GetAllFields(fielddefs):
159 """Extract L{objects.QueryFieldDefinition} from field definitions. 160 161 @rtype: list of L{objects.QueryFieldDefinition} 162 163 """ 164 return [fdef for (fdef, _, _) in fielddefs]
165 166
167 -class Query:
168 - def __init__(self, fieldlist, selected):
169 """Initializes this class. 170 171 The field definition is a dictionary with the field's name as a key and a 172 tuple containing, in order, the field definition object 173 (L{objects.QueryFieldDefinition}, the data kind to help calling code 174 collect data and a retrieval function. The retrieval function is called 175 with two parameters, in order, the data container and the item in container 176 (see L{Query.Query}). 177 178 Users of this class can call L{RequestedData} before preparing the data 179 container to determine what data is needed. 180 181 @type fieldlist: dictionary 182 @param fieldlist: Field definitions 183 @type selected: list of strings 184 @param selected: List of selected fields 185 186 """ 187 self._fields = _GetQueryFields(fieldlist, selected)
188
189 - def RequestedData(self):
190 """Gets requested kinds of data. 191 192 @rtype: frozenset 193 194 """ 195 return frozenset(datakind 196 for (_, datakind, _) in self._fields 197 if datakind is not None)
198
199 - def GetFields(self):
200 """Returns the list of fields for this query. 201 202 Includes unknown fields. 203 204 @rtype: List of L{objects.QueryFieldDefinition} 205 206 """ 207 return GetAllFields(self._fields)
208
209 - def Query(self, ctx):
210 """Execute a query. 211 212 @param ctx: Data container passed to field retrieval functions, must 213 support iteration using C{__iter__} 214 215 """ 216 result = [[_ProcessResult(fn(ctx, item)) for (_, _, fn) in self._fields] 217 for item in ctx] 218 219 # Verify result 220 if __debug__: 221 for row in result: 222 _VerifyResultRow(self._fields, row) 223 224 return result
225
226 - def OldStyleQuery(self, ctx):
227 """Query with "old" query result format. 228 229 See L{Query.Query} for arguments. 230 231 """ 232 unknown = set(fdef.name 233 for (fdef, _, _) in self._fields if fdef.kind == QFT_UNKNOWN) 234 if unknown: 235 raise errors.OpPrereqError("Unknown output fields selected: %s" % 236 (utils.CommaJoin(unknown), ), 237 errors.ECODE_INVAL) 238 239 return [[value for (_, value) in row] 240 for row in self.Query(ctx)]
241 242
243 -def _ProcessResult(value):
244 """Converts result values into externally-visible ones. 245 246 """ 247 if value is _FS_UNKNOWN: 248 return (RS_UNKNOWN, None) 249 elif value is _FS_NODATA: 250 return (RS_NODATA, None) 251 elif value is _FS_UNAVAIL: 252 return (RS_UNAVAIL, None) 253 elif value is _FS_OFFLINE: 254 return (RS_OFFLINE, None) 255 else: 256 return (RS_NORMAL, value)
257 258
259 -def _VerifyResultRow(fields, row):
260 """Verifies the contents of a query result row. 261 262 @type fields: list 263 @param fields: Field definitions for result 264 @type row: list of tuples 265 @param row: Row data 266 267 """ 268 assert len(row) == len(fields) 269 errs = [] 270 for ((status, value), (fdef, _, _)) in zip(row, fields): 271 if status == RS_NORMAL: 272 if not _VERIFY_FN[fdef.kind](value): 273 errs.append("normal field %s fails validation (value is %s)" % 274 (fdef.name, value)) 275 elif value is not None: 276 errs.append("abnormal field %s has a non-None value" % fdef.name) 277 assert not errs, ("Failed validation: %s in row %s" % 278 (utils.CommaJoin(errors), row))
279 280
281 -def _PrepareFieldList(fields, aliases):
282 """Prepares field list for use by L{Query}. 283 284 Converts the list to a dictionary and does some verification. 285 286 @type fields: list of tuples; (L{objects.QueryFieldDefinition}, data 287 kind, retrieval function) 288 @param fields: List of fields, see L{Query.__init__} for a better 289 description 290 @type aliases: list of tuples; (alias, target) 291 @param aliases: list of tuples containing aliases; for each 292 alias/target pair, a duplicate will be created in the field list 293 @rtype: dict 294 @return: Field dictionary for L{Query} 295 296 """ 297 if __debug__: 298 duplicates = utils.FindDuplicates(fdef.title.lower() 299 for (fdef, _, _) in fields) 300 assert not duplicates, "Duplicate title(s) found: %r" % duplicates 301 302 result = {} 303 304 for field in fields: 305 (fdef, _, fn) = field 306 307 assert fdef.name and fdef.title, "Name and title are required" 308 assert FIELD_NAME_RE.match(fdef.name) 309 assert TITLE_RE.match(fdef.title) 310 assert callable(fn) 311 assert fdef.name not in result, \ 312 "Duplicate field name '%s' found" % fdef.name 313 314 result[fdef.name] = field 315 316 for alias, target in aliases: 317 assert alias not in result, "Alias %s overrides an existing field" % alias 318 assert target in result, "Missing target %s for alias %s" % (target, alias) 319 (fdef, k, fn) = result[target] 320 fdef = fdef.Copy() 321 fdef.name = alias 322 result[alias] = (fdef, k, fn) 323 324 assert len(result) == len(fields) + len(aliases) 325 assert compat.all(name == fdef.name 326 for (name, (fdef, _, _)) in result.items()) 327 328 return result
329 330
331 -def GetQueryResponse(query, ctx):
332 """Prepares the response for a query. 333 334 @type query: L{Query} 335 @param ctx: Data container, see L{Query.Query} 336 337 """ 338 return objects.QueryResponse(data=query.Query(ctx), 339 fields=query.GetFields()).ToDict()
340 341
342 -def QueryFields(fielddefs, selected):
343 """Returns list of available fields. 344 345 @type fielddefs: dict 346 @param fielddefs: Field definitions 347 @type selected: list of strings 348 @param selected: List of selected fields 349 @return: List of L{objects.QueryFieldDefinition} 350 351 """ 352 if selected is None: 353 # Client requests all fields, sort by name 354 fdefs = utils.NiceSort(GetAllFields(fielddefs.values()), 355 key=operator.attrgetter("name")) 356 else: 357 # Keep order as requested by client 358 fdefs = Query(fielddefs, selected).GetFields() 359 360 return objects.QueryFieldsResponse(fields=fdefs).ToDict()
361 362
363 -def _MakeField(name, title, kind):
364 """Wrapper for creating L{objects.QueryFieldDefinition} instances. 365 366 @param name: Field name as a regular expression 367 @param title: Human-readable title 368 @param kind: Field type 369 370 """ 371 return objects.QueryFieldDefinition(name=name, title=title, kind=kind)
372 373
374 -def _GetNodeRole(node, master_name):
375 """Determine node role. 376 377 @type node: L{objects.Node} 378 @param node: Node object 379 @type master_name: string 380 @param master_name: Master node name 381 382 """ 383 if node.name == master_name: 384 return "M" 385 elif node.master_candidate: 386 return "C" 387 elif node.drained: 388 return "D" 389 elif node.offline: 390 return "O" 391 else: 392 return "R"
393 394
395 -def _GetItemAttr(attr):
396 """Returns a field function to return an attribute of the item. 397 398 @param attr: Attribute name 399 400 """ 401 getter = operator.attrgetter(attr) 402 return lambda _, item: getter(item)
403 404
405 -def _GetItemTimestamp(getter):
406 """Returns function for getting timestamp of item. 407 408 @type getter: callable 409 @param getter: Function to retrieve timestamp attribute 410 411 """ 412 def fn(_, item): 413 """Returns a timestamp of item. 414 415 """ 416 timestamp = getter(item) 417 if timestamp is None: 418 # Old configs might not have all timestamps 419 return _FS_UNAVAIL 420 else: 421 return timestamp
422 423 return fn 424 425
426 -def _GetItemTimestampFields(datatype):
427 """Returns common timestamp fields. 428 429 @param datatype: Field data type for use by L{Query.RequestedData} 430 431 """ 432 return [ 433 (_MakeField("ctime", "CTime", QFT_TIMESTAMP), datatype, 434 _GetItemTimestamp(operator.attrgetter("ctime"))), 435 (_MakeField("mtime", "MTime", QFT_TIMESTAMP), datatype, 436 _GetItemTimestamp(operator.attrgetter("mtime"))), 437 ]
438 439
440 -class NodeQueryData:
441 """Data container for node data queries. 442 443 """
444 - def __init__(self, nodes, live_data, master_name, node_to_primary, 445 node_to_secondary, groups, oob_support, cluster):
446 """Initializes this class. 447 448 """ 449 self.nodes = nodes 450 self.live_data = live_data 451 self.master_name = master_name 452 self.node_to_primary = node_to_primary 453 self.node_to_secondary = node_to_secondary 454 self.groups = groups 455 self.oob_support = oob_support 456 self.cluster = cluster 457 458 # Used for individual rows 459 self.curlive_data = None
460
461 - def __iter__(self):
462 """Iterate over all nodes. 463 464 This function has side-effects and only one instance of the resulting 465 generator should be used at a time. 466 467 """ 468 for node in self.nodes: 469 if self.live_data: 470 self.curlive_data = self.live_data.get(node.name, None) 471 else: 472 self.curlive_data = None 473 yield node
474 475 476 #: Fields that are direct attributes of an L{objects.Node} object 477 _NODE_SIMPLE_FIELDS = { 478 "drained": ("Drained", QFT_BOOL), 479 "master_candidate": ("MasterC", QFT_BOOL), 480 "master_capable": ("MasterCapable", QFT_BOOL), 481 "name": ("Node", QFT_TEXT), 482 "offline": ("Offline", QFT_BOOL), 483 "serial_no": ("SerialNo", QFT_NUMBER), 484 "uuid": ("UUID", QFT_TEXT), 485 "vm_capable": ("VMCapable", QFT_BOOL), 486 } 487 488 489 #: Fields requiring talking to the node 490 # Note that none of these are available for non-vm_capable nodes 491 _NODE_LIVE_FIELDS = { 492 "bootid": ("BootID", QFT_TEXT, "bootid"), 493 "cnodes": ("CNodes", QFT_NUMBER, "cpu_nodes"), 494 "csockets": ("CSockets", QFT_NUMBER, "cpu_sockets"), 495 "ctotal": ("CTotal", QFT_NUMBER, "cpu_total"), 496 "dfree": ("DFree", QFT_UNIT, "vg_free"), 497 "dtotal": ("DTotal", QFT_UNIT, "vg_size"), 498 "mfree": ("MFree", QFT_UNIT, "memory_free"), 499 "mnode": ("MNode", QFT_UNIT, "memory_dom0"), 500 "mtotal": ("MTotal", QFT_UNIT, "memory_total"), 501 } 502 503
504 -def _GetGroup(cb):
505 """Build function for calling another function with an node group. 506 507 @param cb: The callback to be called with the nodegroup 508 509 """ 510 def fn(ctx, node): 511 """Get group data for a node. 512 513 @type ctx: L{NodeQueryData} 514 @type inst: L{objects.Node} 515 @param inst: Node object 516 517 """ 518 ng = ctx.groups.get(node.group, None) 519 if ng is None: 520 # Nodes always have a group, or the configuration is corrupt 521 return _FS_UNAVAIL 522 523 return cb(ctx, node, ng)
524 525 return fn 526 527
528 -def _GetNodeGroup(ctx, node, ng): # pylint: disable-msg=W0613
529 """Returns the name of a node's group. 530 531 @type ctx: L{NodeQueryData} 532 @type node: L{objects.Node} 533 @param node: Node object 534 @type ng: L{objects.NodeGroup} 535 @param ng: The node group this node belongs to 536 537 """ 538 return ng.name 539 540
541 -def _GetNodePower(ctx, node):
542 """Returns the node powered state 543 544 @type ctx: L{NodeQueryData} 545 @type node: L{objects.Node} 546 @param node: Node object 547 548 """ 549 if ctx.oob_support[node.name]: 550 return node.powered 551 552 return _FS_UNAVAIL
553 554
555 -def _GetNdParams(ctx, node, ng):
556 """Returns the ndparams for this node. 557 558 @type ctx: L{NodeQueryData} 559 @type node: L{objects.Node} 560 @param node: Node object 561 @type ng: L{objects.NodeGroup} 562 @param ng: The node group this node belongs to 563 564 """ 565 return ctx.cluster.SimpleFillND(ng.FillND(node))
566 567
568 -def _GetLiveNodeField(field, kind, ctx, node):
569 """Gets the value of a "live" field from L{NodeQueryData}. 570 571 @param field: Live field name 572 @param kind: Data kind, one of L{constants.QFT_ALL} 573 @type ctx: L{NodeQueryData} 574 @type node: L{objects.Node} 575 @param node: Node object 576 577 """ 578 if node.offline: 579 return _FS_OFFLINE 580 581 if not node.vm_capable: 582 return _FS_UNAVAIL 583 584 if not ctx.curlive_data: 585 return _FS_NODATA 586 587 try: 588 value = ctx.curlive_data[field] 589 except KeyError: 590 return _FS_UNAVAIL 591 592 if kind == QFT_TEXT: 593 return value 594 595 assert kind in (QFT_NUMBER, QFT_UNIT) 596 597 # Try to convert into number 598 try: 599 return int(value) 600 except (ValueError, TypeError): 601 logging.exception("Failed to convert node field '%s' (value %r) to int", 602 value, field) 603 return _FS_UNAVAIL
604 605
606 -def _BuildNodeFields():
607 """Builds list of fields for node queries. 608 609 """ 610 fields = [ 611 (_MakeField("pip", "PrimaryIP", QFT_TEXT), NQ_CONFIG, 612 _GetItemAttr("primary_ip")), 613 (_MakeField("sip", "SecondaryIP", QFT_TEXT), NQ_CONFIG, 614 _GetItemAttr("secondary_ip")), 615 (_MakeField("tags", "Tags", QFT_OTHER), NQ_CONFIG, 616 lambda ctx, node: list(node.GetTags())), 617 (_MakeField("master", "IsMaster", QFT_BOOL), NQ_CONFIG, 618 lambda ctx, node: node.name == ctx.master_name), 619 (_MakeField("role", "Role", QFT_TEXT), NQ_CONFIG, 620 lambda ctx, node: _GetNodeRole(node, ctx.master_name)), 621 (_MakeField("group", "Group", QFT_TEXT), NQ_GROUP, 622 _GetGroup(_GetNodeGroup)), 623 (_MakeField("group.uuid", "GroupUUID", QFT_TEXT), 624 NQ_CONFIG, _GetItemAttr("group")), 625 (_MakeField("powered", "Powered", QFT_BOOL), NQ_OOB, _GetNodePower), 626 (_MakeField("ndparams", "NodeParameters", QFT_OTHER), NQ_GROUP, 627 _GetGroup(_GetNdParams)), 628 (_MakeField("custom_ndparams", "CustomNodeParameters", QFT_OTHER), 629 NQ_GROUP, _GetItemAttr("ndparams")), 630 ] 631 632 def _GetLength(getter): 633 return lambda ctx, node: len(getter(ctx)[node.name])
634 635 def _GetList(getter): 636 return lambda ctx, node: list(getter(ctx)[node.name]) 637 638 # Add fields operating on instance lists 639 for prefix, titleprefix, getter in \ 640 [("p", "Pri", operator.attrgetter("node_to_primary")), 641 ("s", "Sec", operator.attrgetter("node_to_secondary"))]: 642 fields.extend([ 643 (_MakeField("%sinst_cnt" % prefix, "%sinst" % prefix.upper(), QFT_NUMBER), 644 NQ_INST, _GetLength(getter)), 645 (_MakeField("%sinst_list" % prefix, "%sInstances" % titleprefix, 646 QFT_OTHER), 647 NQ_INST, _GetList(getter)), 648 ]) 649 650 # Add simple fields 651 fields.extend([(_MakeField(name, title, kind), NQ_CONFIG, _GetItemAttr(name)) 652 for (name, (title, kind)) in _NODE_SIMPLE_FIELDS.items()]) 653 654 # Add fields requiring live data 655 fields.extend([ 656 (_MakeField(name, title, kind), NQ_LIVE, 657 compat.partial(_GetLiveNodeField, nfield, kind)) 658 for (name, (title, kind, nfield)) in _NODE_LIVE_FIELDS.items() 659 ]) 660 661 # Add timestamps 662 fields.extend(_GetItemTimestampFields(NQ_CONFIG)) 663 664 return _PrepareFieldList(fields, []) 665 666
667 -class InstanceQueryData:
668 """Data container for instance data queries. 669 670 """
671 - def __init__(self, instances, cluster, disk_usage, offline_nodes, bad_nodes, 672 live_data, wrongnode_inst, console):
673 """Initializes this class. 674 675 @param instances: List of instance objects 676 @param cluster: Cluster object 677 @type disk_usage: dict; instance name as key 678 @param disk_usage: Per-instance disk usage 679 @type offline_nodes: list of strings 680 @param offline_nodes: List of offline nodes 681 @type bad_nodes: list of strings 682 @param bad_nodes: List of faulty nodes 683 @type live_data: dict; instance name as key 684 @param live_data: Per-instance live data 685 @type wrongnode_inst: set 686 @param wrongnode_inst: Set of instances running on wrong node(s) 687 @type console: dict; instance name as key 688 @param console: Per-instance console information 689 690 """ 691 assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \ 692 "Offline nodes not included in bad nodes" 693 assert not (set(live_data.keys()) & set(bad_nodes)), \ 694 "Found live data for bad or offline nodes" 695 696 self.instances = instances 697 self.cluster = cluster 698 self.disk_usage = disk_usage 699 self.offline_nodes = offline_nodes 700 self.bad_nodes = bad_nodes 701 self.live_data = live_data 702 self.wrongnode_inst = wrongnode_inst 703 self.console = console 704 705 # Used for individual rows 706 self.inst_hvparams = None 707 self.inst_beparams = None 708 self.inst_nicparams = None
709
710 - def __iter__(self):
711 """Iterate over all instances. 712 713 This function has side-effects and only one instance of the resulting 714 generator should be used at a time. 715 716 """ 717 for inst in self.instances: 718 self.inst_hvparams = self.cluster.FillHV(inst, skip_globals=True) 719 self.inst_beparams = self.cluster.FillBE(inst) 720 self.inst_nicparams = [self.cluster.SimpleFillNIC(nic.nicparams) 721 for nic in inst.nics] 722 723 yield inst
724 725
726 -def _GetInstOperState(ctx, inst):
727 """Get instance's operational status. 728 729 @type ctx: L{InstanceQueryData} 730 @type inst: L{objects.Instance} 731 @param inst: Instance object 732 733 """ 734 # Can't use RS_OFFLINE here as it would describe the instance to 735 # be offline when we actually don't know due to missing data 736 if inst.primary_node in ctx.bad_nodes: 737 return _FS_NODATA 738 else: 739 return bool(ctx.live_data.get(inst.name))
740 741
742 -def _GetInstLiveData(name):
743 """Build function for retrieving live data. 744 745 @type name: string 746 @param name: Live data field name 747 748 """ 749 def fn(ctx, inst): 750 """Get live data for an instance. 751 752 @type ctx: L{InstanceQueryData} 753 @type inst: L{objects.Instance} 754 @param inst: Instance object 755 756 """ 757 if (inst.primary_node in ctx.bad_nodes or 758 inst.primary_node in ctx.offline_nodes): 759 # Can't use RS_OFFLINE here as it would describe the instance to be 760 # offline when we actually don't know due to missing data 761 return _FS_NODATA 762 763 if inst.name in ctx.live_data: 764 data = ctx.live_data[inst.name] 765 if name in data: 766 return data[name] 767 768 return _FS_UNAVAIL
769 770 return fn 771 772
773 -def _GetInstStatus(ctx, inst):
774 """Get instance status. 775 776 @type ctx: L{InstanceQueryData} 777 @type inst: L{objects.Instance} 778 @param inst: Instance object 779 780 """ 781 if inst.primary_node in ctx.offline_nodes: 782 return "ERROR_nodeoffline" 783 784 if inst.primary_node in ctx.bad_nodes: 785 return "ERROR_nodedown" 786 787 if bool(ctx.live_data.get(inst.name)): 788 if inst.name in ctx.wrongnode_inst: 789 return "ERROR_wrongnode" 790 elif inst.admin_up: 791 return "running" 792 else: 793 return "ERROR_up" 794 795 if inst.admin_up: 796 return "ERROR_down" 797 798 return "ADMIN_down"
799 800
801 -def _GetInstDiskSize(index):
802 """Build function for retrieving disk size. 803 804 @type index: int 805 @param index: Disk index 806 807 """ 808 def fn(_, inst): 809 """Get size of a disk. 810 811 @type inst: L{objects.Instance} 812 @param inst: Instance object 813 814 """ 815 try: 816 return inst.disks[index].size 817 except IndexError: 818 return _FS_UNAVAIL
819 820 return fn 821 822
823 -def _GetInstNic(index, cb):
824 """Build function for calling another function with an instance NIC. 825 826 @type index: int 827 @param index: NIC index 828 @type cb: callable 829 @param cb: Callback 830 831 """ 832 def fn(ctx, inst): 833 """Call helper function with instance NIC. 834 835 @type ctx: L{InstanceQueryData} 836 @type inst: L{objects.Instance} 837 @param inst: Instance object 838 839 """ 840 try: 841 nic = inst.nics[index] 842 except IndexError: 843 return _FS_UNAVAIL 844 845 return cb(ctx, index, nic)
846 847 return fn 848 849
850 -def _GetInstNicIp(ctx, _, nic): # pylint: disable-msg=W0613
851 """Get a NIC's IP address. 852 853 @type ctx: L{InstanceQueryData} 854 @type nic: L{objects.NIC} 855 @param nic: NIC object 856 857 """ 858 if nic.ip is None: 859 return _FS_UNAVAIL 860 else: 861 return nic.ip 862 863
864 -def _GetInstNicBridge(ctx, index, _):
865 """Get a NIC's bridge. 866 867 @type ctx: L{InstanceQueryData} 868 @type index: int 869 @param index: NIC index 870 871 """ 872 assert len(ctx.inst_nicparams) >= index 873 874 nicparams = ctx.inst_nicparams[index] 875 876 if nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED: 877 return nicparams[constants.NIC_LINK] 878 else: 879 return _FS_UNAVAIL
880 881
882 -def _GetInstAllNicBridges(ctx, inst):
883 """Get all network bridges for an instance. 884 885 @type ctx: L{InstanceQueryData} 886 @type inst: L{objects.Instance} 887 @param inst: Instance object 888 889 """ 890 assert len(ctx.inst_nicparams) == len(inst.nics) 891 892 result = [] 893 894 for nicp in ctx.inst_nicparams: 895 if nicp[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED: 896 result.append(nicp[constants.NIC_LINK]) 897 else: 898 result.append(None) 899 900 assert len(result) == len(inst.nics) 901 902 return result
903 904
905 -def _GetInstNicParam(name):
906 """Build function for retrieving a NIC parameter. 907 908 @type name: string 909 @param name: Parameter name 910 911 """ 912 def fn(ctx, index, _): 913 """Get a NIC's bridge. 914 915 @type ctx: L{InstanceQueryData} 916 @type inst: L{objects.Instance} 917 @param inst: Instance object 918 @type nic: L{objects.NIC} 919 @param nic: NIC object 920 921 """ 922 assert len(ctx.inst_nicparams) >= index 923 return ctx.inst_nicparams[index][name]
924 925 return fn 926 927
928 -def _GetInstanceNetworkFields():
929 """Get instance fields involving network interfaces. 930 931 @return: List of field definitions used as input for L{_PrepareFieldList} 932 933 """ 934 nic_mac_fn = lambda ctx, _, nic: nic.mac 935 nic_mode_fn = _GetInstNicParam(constants.NIC_MODE) 936 nic_link_fn = _GetInstNicParam(constants.NIC_LINK) 937 938 fields = [ 939 # First NIC (legacy) 940 (_MakeField("ip", "IP_address", QFT_TEXT), IQ_CONFIG, 941 _GetInstNic(0, _GetInstNicIp)), 942 (_MakeField("mac", "MAC_address", QFT_TEXT), IQ_CONFIG, 943 _GetInstNic(0, nic_mac_fn)), 944 (_MakeField("bridge", "Bridge", QFT_TEXT), IQ_CONFIG, 945 _GetInstNic(0, _GetInstNicBridge)), 946 (_MakeField("nic_mode", "NIC_Mode", QFT_TEXT), IQ_CONFIG, 947 _GetInstNic(0, nic_mode_fn)), 948 (_MakeField("nic_link", "NIC_Link", QFT_TEXT), IQ_CONFIG, 949 _GetInstNic(0, nic_link_fn)), 950 951 # All NICs 952 (_MakeField("nic.count", "NICs", QFT_NUMBER), IQ_CONFIG, 953 lambda ctx, inst: len(inst.nics)), 954 (_MakeField("nic.macs", "NIC_MACs", QFT_OTHER), IQ_CONFIG, 955 lambda ctx, inst: [nic.mac for nic in inst.nics]), 956 (_MakeField("nic.ips", "NIC_IPs", QFT_OTHER), IQ_CONFIG, 957 lambda ctx, inst: [nic.ip for nic in inst.nics]), 958 (_MakeField("nic.modes", "NIC_modes", QFT_OTHER), IQ_CONFIG, 959 lambda ctx, inst: [nicp[constants.NIC_MODE] 960 for nicp in ctx.inst_nicparams]), 961 (_MakeField("nic.links", "NIC_links", QFT_OTHER), IQ_CONFIG, 962 lambda ctx, inst: [nicp[constants.NIC_LINK] 963 for nicp in ctx.inst_nicparams]), 964 (_MakeField("nic.bridges", "NIC_bridges", QFT_OTHER), IQ_CONFIG, 965 _GetInstAllNicBridges), 966 ] 967 968 # NICs by number 969 for i in range(constants.MAX_NICS): 970 fields.extend([ 971 (_MakeField("nic.ip/%s" % i, "NicIP/%s" % i, QFT_TEXT), 972 IQ_CONFIG, _GetInstNic(i, _GetInstNicIp)), 973 (_MakeField("nic.mac/%s" % i, "NicMAC/%s" % i, QFT_TEXT), 974 IQ_CONFIG, _GetInstNic(i, nic_mac_fn)), 975 (_MakeField("nic.mode/%s" % i, "NicMode/%s" % i, QFT_TEXT), 976 IQ_CONFIG, _GetInstNic(i, nic_mode_fn)), 977 (_MakeField("nic.link/%s" % i, "NicLink/%s" % i, QFT_TEXT), 978 IQ_CONFIG, _GetInstNic(i, nic_link_fn)), 979 (_MakeField("nic.bridge/%s" % i, "NicBridge/%s" % i, QFT_TEXT), 980 IQ_CONFIG, _GetInstNic(i, _GetInstNicBridge)), 981 ]) 982 983 return fields
984 985
986 -def _GetInstDiskUsage(ctx, inst):
987 """Get disk usage for an instance. 988 989 @type ctx: L{InstanceQueryData} 990 @type inst: L{objects.Instance} 991 @param inst: Instance object 992 993 """ 994 usage = ctx.disk_usage[inst.name] 995 996 if usage is None: 997 usage = 0 998 999 return usage
1000 1001
1002 -def _GetInstanceConsole(ctx, inst):
1003 """Get console information for instance. 1004 1005 @type ctx: L{InstanceQueryData} 1006 @type inst: L{objects.Instance} 1007 @param inst: Instance object 1008 1009 """ 1010 consinfo = ctx.console[inst.name] 1011 1012 if consinfo is None: 1013 return _FS_UNAVAIL 1014 1015 return consinfo
1016 1017
1018 -def _GetInstanceDiskFields():
1019 """Get instance fields involving disks. 1020 1021 @return: List of field definitions used as input for L{_PrepareFieldList} 1022 1023 """ 1024 fields = [ 1025 (_MakeField("disk_usage", "DiskUsage", QFT_UNIT), IQ_DISKUSAGE, 1026 _GetInstDiskUsage), 1027 (_MakeField("disk.count", "Disks", QFT_NUMBER), IQ_CONFIG, 1028 lambda ctx, inst: len(inst.disks)), 1029 (_MakeField("disk.sizes", "Disk_sizes", QFT_OTHER), IQ_CONFIG, 1030 lambda ctx, inst: [disk.size for disk in inst.disks]), 1031 ] 1032 1033 # Disks by number 1034 fields.extend([ 1035 (_MakeField("disk.size/%s" % i, "Disk/%s" % i, QFT_UNIT), 1036 IQ_CONFIG, _GetInstDiskSize(i)) 1037 for i in range(constants.MAX_DISKS) 1038 ]) 1039 1040 return fields
1041 1042
1043 -def _GetInstanceParameterFields():
1044 """Get instance fields involving parameters. 1045 1046 @return: List of field definitions used as input for L{_PrepareFieldList} 1047 1048 """ 1049 # TODO: Consider moving titles closer to constants 1050 be_title = { 1051 constants.BE_AUTO_BALANCE: "Auto_balance", 1052 constants.BE_MEMORY: "ConfigMemory", 1053 constants.BE_VCPUS: "ConfigVCPUs", 1054 } 1055 1056 hv_title = { 1057 constants.HV_ACPI: "ACPI", 1058 constants.HV_BOOT_ORDER: "Boot_order", 1059 constants.HV_CDROM_IMAGE_PATH: "CDROM_image_path", 1060 constants.HV_DISK_TYPE: "Disk_type", 1061 constants.HV_INITRD_PATH: "Initrd_path", 1062 constants.HV_KERNEL_PATH: "Kernel_path", 1063 constants.HV_NIC_TYPE: "NIC_type", 1064 constants.HV_PAE: "PAE", 1065 constants.HV_VNC_BIND_ADDRESS: "VNC_bind_address", 1066 } 1067 1068 fields = [ 1069 # Filled parameters 1070 (_MakeField("hvparams", "HypervisorParameters", QFT_OTHER), 1071 IQ_CONFIG, lambda ctx, _: ctx.inst_hvparams), 1072 (_MakeField("beparams", "BackendParameters", QFT_OTHER), 1073 IQ_CONFIG, lambda ctx, _: ctx.inst_beparams), 1074 1075 # Unfilled parameters 1076 (_MakeField("custom_hvparams", "CustomHypervisorParameters", QFT_OTHER), 1077 IQ_CONFIG, _GetItemAttr("hvparams")), 1078 (_MakeField("custom_beparams", "CustomBackendParameters", QFT_OTHER), 1079 IQ_CONFIG, _GetItemAttr("beparams")), 1080 (_MakeField("custom_nicparams", "CustomNicParameters", QFT_OTHER), 1081 IQ_CONFIG, lambda ctx, inst: [nic.nicparams for nic in inst.nics]), 1082 ] 1083 1084 # HV params 1085 def _GetInstHvParam(name): 1086 return lambda ctx, _: ctx.inst_hvparams.get(name, _FS_UNAVAIL)
1087 1088 fields.extend([ 1089 (_MakeField("hv/%s" % name, hv_title.get(name, "hv/%s" % name), 1090 _VTToQFT[kind]), 1091 IQ_CONFIG, _GetInstHvParam(name)) 1092 for name, kind in constants.HVS_PARAMETER_TYPES.items() 1093 if name not in constants.HVC_GLOBALS 1094 ]) 1095 1096 # BE params 1097 def _GetInstBeParam(name): 1098 return lambda ctx, _: ctx.inst_beparams.get(name, None) 1099 1100 fields.extend([ 1101 (_MakeField("be/%s" % name, be_title.get(name, "be/%s" % name), 1102 _VTToQFT[kind]), IQ_CONFIG, 1103 _GetInstBeParam(name)) 1104 for name, kind in constants.BES_PARAMETER_TYPES.items() 1105 ]) 1106 1107 return fields 1108 1109 1110 _INST_SIMPLE_FIELDS = { 1111 "disk_template": ("Disk_template", QFT_TEXT), 1112 "hypervisor": ("Hypervisor", QFT_TEXT), 1113 "name": ("Instance", QFT_TEXT), 1114 # Depending on the hypervisor, the port can be None 1115 "network_port": ("Network_port", QFT_OTHER), 1116 "os": ("OS", QFT_TEXT), 1117 "serial_no": ("SerialNo", QFT_NUMBER), 1118 "uuid": ("UUID", QFT_TEXT), 1119 } 1120 1121
1122 -def _BuildInstanceFields():
1123 """Builds list of fields for instance queries. 1124 1125 """ 1126 fields = [ 1127 (_MakeField("pnode", "Primary_node", QFT_TEXT), IQ_CONFIG, 1128 _GetItemAttr("primary_node")), 1129 (_MakeField("snodes", "Secondary_Nodes", QFT_OTHER), IQ_CONFIG, 1130 lambda ctx, inst: list(inst.secondary_nodes)), 1131 (_MakeField("admin_state", "Autostart", QFT_BOOL), IQ_CONFIG, 1132 _GetItemAttr("admin_up")), 1133 (_MakeField("tags", "Tags", QFT_OTHER), IQ_CONFIG, 1134 lambda ctx, inst: list(inst.GetTags())), 1135 (_MakeField("console", "Console", QFT_OTHER), IQ_CONSOLE, 1136 _GetInstanceConsole), 1137 ] 1138 1139 # Add simple fields 1140 fields.extend([(_MakeField(name, title, kind), IQ_CONFIG, _GetItemAttr(name)) 1141 for (name, (title, kind)) in _INST_SIMPLE_FIELDS.items()]) 1142 1143 # Fields requiring talking to the node 1144 fields.extend([ 1145 (_MakeField("oper_state", "Running", QFT_BOOL), IQ_LIVE, 1146 _GetInstOperState), 1147 (_MakeField("oper_ram", "Memory", QFT_UNIT), IQ_LIVE, 1148 _GetInstLiveData("memory")), 1149 (_MakeField("oper_vcpus", "VCPUs", QFT_NUMBER), IQ_LIVE, 1150 _GetInstLiveData("vcpus")), 1151 (_MakeField("status", "Status", QFT_TEXT), IQ_LIVE, _GetInstStatus), 1152 ]) 1153 1154 fields.extend(_GetInstanceParameterFields()) 1155 fields.extend(_GetInstanceDiskFields()) 1156 fields.extend(_GetInstanceNetworkFields()) 1157 fields.extend(_GetItemTimestampFields(IQ_CONFIG)) 1158 1159 aliases = [ 1160 ("vcpus", "be/vcpus"), 1161 ("sda_size", "disk.size/0"), 1162 ("sdb_size", "disk.size/1"), 1163 ] 1164 1165 return _PrepareFieldList(fields, aliases)
1166 1167
1168 -class LockQueryData:
1169 """Data container for lock data queries. 1170 1171 """
1172 - def __init__(self, lockdata):
1173 """Initializes this class. 1174 1175 """ 1176 self.lockdata = lockdata
1177
1178 - def __iter__(self):
1179 """Iterate over all locks. 1180 1181 """ 1182 return iter(self.lockdata)
1183 1184
1185 -def _GetLockOwners(_, data):
1186 """Returns a sorted list of a lock's current owners. 1187 1188 """ 1189 (_, _, owners, _) = data 1190 1191 if owners: 1192 owners = utils.NiceSort(owners) 1193 1194 return owners
1195 1196
1197 -def _GetLockPending(_, data):
1198 """Returns a sorted list of a lock's pending acquires. 1199 1200 """ 1201 (_, _, _, pending) = data 1202 1203 if pending: 1204 pending = [(mode, utils.NiceSort(names)) 1205 for (mode, names) in pending] 1206 1207 return pending
1208 1209
1210 -def _BuildLockFields():
1211 """Builds list of fields for lock queries. 1212 1213 """ 1214 return _PrepareFieldList([ 1215 (_MakeField("name", "Name", QFT_TEXT), None, 1216 lambda ctx, (name, mode, owners, pending): name), 1217 (_MakeField("mode", "Mode", QFT_OTHER), LQ_MODE, 1218 lambda ctx, (name, mode, owners, pending): mode), 1219 (_MakeField("owner", "Owner", QFT_OTHER), LQ_OWNER, _GetLockOwners), 1220 (_MakeField("pending", "Pending", QFT_OTHER), LQ_PENDING, _GetLockPending), 1221 ], [])
1222 1223
1224 -class GroupQueryData:
1225 """Data container for node group data queries. 1226 1227 """
1228 - def __init__(self, groups, group_to_nodes, group_to_instances):
1229 """Initializes this class. 1230 1231 @param groups: List of node group objects 1232 @type group_to_nodes: dict; group UUID as key 1233 @param group_to_nodes: Per-group list of nodes 1234 @type group_to_instances: dict; group UUID as key 1235 @param group_to_instances: Per-group list of (primary) instances 1236 1237 """ 1238 self.groups = groups 1239 self.group_to_nodes = group_to_nodes 1240 self.group_to_instances = group_to_instances
1241
1242 - def __iter__(self):
1243 """Iterate over all node groups. 1244 1245 """ 1246 return iter(self.groups)
1247 1248 1249 _GROUP_SIMPLE_FIELDS = { 1250 "alloc_policy": ("AllocPolicy", QFT_TEXT), 1251 "name": ("Group", QFT_TEXT), 1252 "serial_no": ("SerialNo", QFT_NUMBER), 1253 "uuid": ("UUID", QFT_TEXT), 1254 "ndparams": ("NDParams", QFT_OTHER), 1255 } 1256 1257
1258 -def _BuildGroupFields():
1259 """Builds list of fields for node group queries. 1260 1261 """ 1262 # Add simple fields 1263 fields = [(_MakeField(name, title, kind), GQ_CONFIG, _GetItemAttr(name)) 1264 for (name, (title, kind)) in _GROUP_SIMPLE_FIELDS.items()] 1265 1266 def _GetLength(getter): 1267 return lambda ctx, group: len(getter(ctx)[group.uuid])
1268 1269 def _GetSortedList(getter): 1270 return lambda ctx, group: utils.NiceSort(getter(ctx)[group.uuid]) 1271 1272 group_to_nodes = operator.attrgetter("group_to_nodes") 1273 group_to_instances = operator.attrgetter("group_to_instances") 1274 1275 # Add fields for nodes 1276 fields.extend([ 1277 (_MakeField("node_cnt", "Nodes", QFT_NUMBER), 1278 GQ_NODE, _GetLength(group_to_nodes)), 1279 (_MakeField("node_list", "NodeList", QFT_OTHER), 1280 GQ_NODE, _GetSortedList(group_to_nodes)), 1281 ]) 1282 1283 # Add fields for instances 1284 fields.extend([ 1285 (_MakeField("pinst_cnt", "Instances", QFT_NUMBER), 1286 GQ_INST, _GetLength(group_to_instances)), 1287 (_MakeField("pinst_list", "InstanceList", QFT_OTHER), 1288 GQ_INST, _GetSortedList(group_to_instances)), 1289 ]) 1290 1291 fields.extend(_GetItemTimestampFields(GQ_CONFIG)) 1292 1293 return _PrepareFieldList(fields, []) 1294 1295 1296 #: Fields available for node queries 1297 NODE_FIELDS = _BuildNodeFields() 1298 1299 #: Fields available for instance queries 1300 INSTANCE_FIELDS = _BuildInstanceFields() 1301 1302 #: Fields available for lock queries 1303 LOCK_FIELDS = _BuildLockFields() 1304 1305 #: Fields available for node group queries 1306 GROUP_FIELDS = _BuildGroupFields() 1307 1308 #: All available field lists 1309 ALL_FIELD_LISTS = [NODE_FIELDS, INSTANCE_FIELDS, LOCK_FIELDS, GROUP_FIELDS] 1310