Small improvements for cluster verify
[ganeti-local] / lib / query.py
index a462b7b..ae7da5a 100644 (file)
@@ -83,7 +83,8 @@ from ganeti.constants import (QFT_UNKNOWN, QFT_TEXT, QFT_BOOL, QFT_NUMBER,
 (IQ_CONFIG,
  IQ_LIVE,
  IQ_DISKUSAGE,
- IQ_CONSOLE) = range(100, 104)
+ IQ_CONSOLE,
+ IQ_NODES) = range(100, 105)
 
 (LQ_MODE,
  LQ_OWNER,
@@ -110,7 +111,7 @@ _VERIFY_FN = {
   QFT_BOOL: ht.TBool,
   QFT_NUMBER: ht.TInt,
   QFT_UNIT: ht.TInt,
-  QFT_TIMESTAMP: ht.TOr(ht.TInt, ht.TFloat),
+  QFT_TIMESTAMP: ht.TNumber,
   QFT_OTHER: lambda _: True,
   }
 
@@ -322,6 +323,16 @@ def _WrapNot(fn, lhs, rhs):
   return not fn(lhs, rhs)
 
 
+def _PrepareRegex(pattern):
+  """Compiles a regular expression.
+
+  """
+  try:
+    return re.compile(pattern)
+  except re.error, err:
+    raise errors.ParameterError("Invalid regex pattern (%s)" % err)
+
+
 class _FilterCompilerHelper:
   """Converts a query filter to a callable usable for filtering.
 
@@ -349,8 +360,9 @@ class _FilterCompilerHelper:
   _EQUALITY_CHECKS = [
     (QFF_HOSTNAME,
      lambda lhs, rhs: utils.MatchNameComponent(rhs, [lhs],
-                                               case_sensitive=False)),
-    (None, operator.eq),
+                                               case_sensitive=False),
+     None),
+    (None, operator.eq, None),
     ]
 
   """Known operators
@@ -377,12 +389,13 @@ class _FilterCompilerHelper:
     # Binary operators
     qlang.OP_EQUAL: (_OPTYPE_BINARY, _EQUALITY_CHECKS),
     qlang.OP_NOT_EQUAL:
-      (_OPTYPE_BINARY, [(flags, compat.partial(_WrapNot, fn))
-                        for (flags, fn) in _EQUALITY_CHECKS]),
-    qlang.OP_GLOB: (_OPTYPE_BINARY, NotImplemented),
-    qlang.OP_REGEXP: (_OPTYPE_BINARY, NotImplemented),
+      (_OPTYPE_BINARY, [(flags, compat.partial(_WrapNot, fn), valprepfn)
+                        for (flags, fn, valprepfn) in _EQUALITY_CHECKS]),
+    qlang.OP_REGEXP: (_OPTYPE_BINARY, [
+      (None, lambda lhs, rhs: rhs.search(lhs), _PrepareRegex),
+      ]),
     qlang.OP_CONTAINS: (_OPTYPE_BINARY, [
-      (None, operator.contains),
+      (None, operator.contains, None),
       ]),
     }
 
@@ -556,8 +569,12 @@ class _FilterCompilerHelper:
     if hints_fn:
       hints_fn(op, datakind, name, value)
 
-    for (fn_flags, fn) in op_data:
+    for (fn_flags, fn, valprepfn) in op_data:
       if fn_flags is None or fn_flags & field_flags:
+        # Prepare value if necessary (e.g. compile regular expression)
+        if valprepfn:
+          value = valprepfn(value)
+
         return compat.partial(_WrapBinaryOp, fn, retrieval_fn, value)
 
     raise errors.ProgrammerError("Unable to find operator implementation"
@@ -1207,7 +1224,7 @@ class InstanceQueryData:
 
   """
   def __init__(self, instances, cluster, disk_usage, offline_nodes, bad_nodes,
-               live_data, wrongnode_inst, console):
+               live_data, wrongnode_inst, console, nodes, groups):
     """Initializes this class.
 
     @param instances: List of instance objects
@@ -1224,6 +1241,8 @@ class InstanceQueryData:
     @param wrongnode_inst: Set of instances running on wrong node(s)
     @type console: dict; instance name as key
     @param console: Per-instance console information
+    @type nodes: dict; node name as key
+    @param nodes: Node objects
 
     """
     assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
@@ -1239,10 +1258,13 @@ class InstanceQueryData:
     self.live_data = live_data
     self.wrongnode_inst = wrongnode_inst
     self.console = console
+    self.nodes = nodes
+    self.groups = groups
 
     # Used for individual rows
     self.inst_hvparams = None
     self.inst_beparams = None
+    self.inst_osparams = None
     self.inst_nicparams = None
 
   def __iter__(self):
@@ -1255,6 +1277,7 @@ class InstanceQueryData:
     for inst in self.instances:
       self.inst_hvparams = self.cluster.FillHV(inst, skip_globals=True)
       self.inst_beparams = self.cluster.FillBE(inst)
+      self.inst_osparams = self.cluster.SimpleFillOS(inst.os, inst.osparams)
       self.inst_nicparams = [self.cluster.SimpleFillNIC(nic.nicparams)
                              for nic in inst.nics]
 
@@ -1620,11 +1643,14 @@ def _GetInstanceParameterFields():
   fields = [
     # Filled parameters
     (_MakeField("hvparams", "HypervisorParameters", QFT_OTHER,
-                "Hypervisor parameters"),
+                "Hypervisor parameters (merged)"),
      IQ_CONFIG, 0, lambda ctx, _: ctx.inst_hvparams),
     (_MakeField("beparams", "BackendParameters", QFT_OTHER,
-                "Backend parameters"),
+                "Backend parameters (merged)"),
      IQ_CONFIG, 0, lambda ctx, _: ctx.inst_beparams),
+    (_MakeField("osparams", "OpSysParameters", QFT_OTHER,
+                "Operating system parameters (merged)"),
+     IQ_CONFIG, 0, lambda ctx, _: ctx.inst_osparams),
 
     # Unfilled parameters
     (_MakeField("custom_hvparams", "CustomHypervisorParameters", QFT_OTHER,
@@ -1633,6 +1659,9 @@ def _GetInstanceParameterFields():
     (_MakeField("custom_beparams", "CustomBackendParameters", QFT_OTHER,
                 "Custom backend parameters",),
      IQ_CONFIG, 0, _GetItemAttr("beparams")),
+    (_MakeField("custom_osparams", "CustomOpSysParameters", QFT_OTHER,
+                "Custom operating system parameters",),
+     IQ_CONFIG, 0, _GetItemAttr("osparams")),
     (_MakeField("custom_nicparams", "CustomNicParameters", QFT_OTHER,
                 "Custom network interface parameters"),
      IQ_CONFIG, 0, lambda ctx, inst: [nic.nicparams for nic in inst.nics]),
@@ -1677,6 +1706,45 @@ _INST_SIMPLE_FIELDS = {
   }
 
 
+def _GetInstNodeGroup(ctx, default, node_name):
+  """Gets group UUID of an instance node.
+
+  @type ctx: L{InstanceQueryData}
+  @param default: Default value
+  @type node_name: string
+  @param node_name: Node name
+
+  """
+  try:
+    node = ctx.nodes[node_name]
+  except KeyError:
+    return default
+  else:
+    return node.group
+
+
+def _GetInstNodeGroupName(ctx, default, node_name):
+  """Gets group name of an instance node.
+
+  @type ctx: L{InstanceQueryData}
+  @param default: Default value
+  @type node_name: string
+  @param node_name: Node name
+
+  """
+  try:
+    node = ctx.nodes[node_name]
+  except KeyError:
+    return default
+
+  try:
+    group = ctx.groups[node.group]
+  except KeyError:
+    return default
+
+  return group.name
+
+
 def _BuildInstanceFields():
   """Builds list of fields for instance queries.
 
@@ -1684,10 +1752,29 @@ def _BuildInstanceFields():
   fields = [
     (_MakeField("pnode", "Primary_node", QFT_TEXT, "Primary node"),
      IQ_CONFIG, QFF_HOSTNAME, _GetItemAttr("primary_node")),
+    (_MakeField("pnode.group", "PrimaryNodeGroup", QFT_TEXT,
+                "Primary node's group"),
+     IQ_NODES, 0,
+     lambda ctx, inst: _GetInstNodeGroupName(ctx, _FS_UNAVAIL,
+                                             inst.primary_node)),
+    (_MakeField("pnode.group.uuid", "PrimaryNodeGroupUUID", QFT_TEXT,
+                "Primary node's group UUID"),
+     IQ_NODES, 0,
+     lambda ctx, inst: _GetInstNodeGroup(ctx, _FS_UNAVAIL, inst.primary_node)),
     # TODO: Allow filtering by secondary node as hostname
     (_MakeField("snodes", "Secondary_Nodes", QFT_OTHER,
                 "Secondary nodes; usually this will just be one node"),
      IQ_CONFIG, 0, lambda ctx, inst: list(inst.secondary_nodes)),
+    (_MakeField("snodes.group", "SecondaryNodesGroups", QFT_OTHER,
+                "Node groups of secondary nodes"),
+     IQ_NODES, 0,
+     lambda ctx, inst: map(compat.partial(_GetInstNodeGroupName, ctx, None),
+                           inst.secondary_nodes)),
+    (_MakeField("snodes.group.uuid", "SecondaryNodesGroupsUUID", QFT_OTHER,
+                "Node group UUIDs of secondary nodes"),
+     IQ_NODES, 0,
+     lambda ctx, inst: map(compat.partial(_GetInstNodeGroup, ctx, None),
+                           inst.secondary_nodes)),
     (_MakeField("admin_state", "Autostart", QFT_BOOL,
                 "Desired state of instance (if set, the instance should be"
                 " up)"),
@@ -1883,6 +1970,12 @@ def _BuildGroupFields():
      GQ_INST, 0, _GetSortedList(group_to_instances)),
     ])
 
+  # Other fields
+  fields.extend([
+    (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), GQ_CONFIG, 0,
+     lambda ctx, group: list(group.GetTags())),
+    ])
+
   fields.extend(_GetItemTimestampFields(GQ_CONFIG))
 
   return _PrepareFieldList(fields, [])
@@ -1925,7 +2018,8 @@ def _BuildOsFields():
      None, 0, _ConvWrap(sorted, _GetItemAttr("api_versions"))),
     (_MakeField("parameters", "Parameters", QFT_OTHER,
                 "Operating system parameters"),
-     None, 0, _ConvWrap(utils.NiceSort, _GetItemAttr("parameters"))),
+     None, 0, _ConvWrap(compat.partial(utils.NiceSort, key=compat.fst),
+                        _GetItemAttr("parameters"))),
     (_MakeField("node_status", "NodeStatus", QFT_OTHER,
                 "Status from node"),
      None, 0, _GetItemAttr("node_status")),