Extract os related logical units from cmdlib
authorThomas Thrainer <thomasth@google.com>
Tue, 14 May 2013 08:17:04 +0000 (10:17 +0200)
committerThomas Thrainer <thomasth@google.com>
Fri, 17 May 2013 09:32:03 +0000 (11:32 +0200)
All LUOs* classes are extracted to operating_system.py.

Signed-off-by: Thomas Thrainer <thomasth@google.com>
Reviewed-by: Bernardo Dal Seno <bdalseno@google.com>

Makefile.am
lib/cmdlib/__init__.py
lib/cmdlib/operating_system.py [new file with mode: 0644]
lib/cmdlib/query.py

index da01b58..9a36e4d 100644 (file)
@@ -318,6 +318,7 @@ cmdlib_PYTHON = \
        lib/cmdlib/instance_utils.py \
        lib/cmdlib/backup.py \
        lib/cmdlib/query.py \
+       lib/cmdlib/operating_system.py \
        lib/cmdlib/tags.py \
        lib/cmdlib/network.py \
        lib/cmdlib/test.py
index 5b43c02..95c7a42 100644 (file)
@@ -82,6 +82,7 @@ from ganeti.cmdlib.instance import LUInstanceCreate, LUInstanceRename, \
 from ganeti.cmdlib.backup import LUBackupQuery, LUBackupPrepare, \
   LUBackupExport, LUBackupRemove
 from ganeti.cmdlib.query import LUQuery, LUQueryFields
+from ganeti.cmdlib.operating_system import LUOsDiagnose
 from ganeti.cmdlib.tags import LUTagsGet, LUTagsSearch, LUTagsSet, LUTagsDel
 from ganeti.cmdlib.network import LUNetworkAdd, LUNetworkRemove, \
   LUNetworkSetParams, LUNetworkQuery, LUNetworkConnect, LUNetworkDisconnect
@@ -274,167 +275,6 @@ class LUOobCommand(NoHooksLU):
                                utils.CommaJoin(errs))
 
 
-class _OsQuery(_QueryBase):
-  FIELDS = query.OS_FIELDS
-
-  def ExpandNames(self, lu):
-    # Lock all nodes in shared mode
-    # Temporary removal of locks, should be reverted later
-    # TODO: reintroduce locks when they are lighter-weight
-    lu.needed_locks = {}
-    #self.share_locks[locking.LEVEL_NODE] = 1
-    #self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
-
-    # The following variables interact with _QueryBase._GetNames
-    if self.names:
-      self.wanted = self.names
-    else:
-      self.wanted = locking.ALL_SET
-
-    self.do_locking = self.use_locking
-
-  def DeclareLocks(self, lu, level):
-    pass
-
-  @staticmethod
-  def _DiagnoseByOS(rlist):
-    """Remaps a per-node return list into an a per-os per-node dictionary
-
-    @param rlist: a map with node names as keys and OS objects as values
-
-    @rtype: dict
-    @return: a dictionary with osnames as keys and as value another
-        map, with nodes as keys and tuples of (path, status, diagnose,
-        variants, parameters, api_versions) as values, eg::
-
-          {"debian-etch": {"node1": [(/usr/lib/..., True, "", [], []),
-                                     (/srv/..., False, "invalid api")],
-                           "node2": [(/srv/..., True, "", [], [])]}
-          }
-
-    """
-    all_os = {}
-    # we build here the list of nodes that didn't fail the RPC (at RPC
-    # level), so that nodes with a non-responding node daemon don't
-    # make all OSes invalid
-    good_nodes = [node_name for node_name in rlist
-                  if not rlist[node_name].fail_msg]
-    for node_name, nr in rlist.items():
-      if nr.fail_msg or not nr.payload:
-        continue
-      for (name, path, status, diagnose, variants,
-           params, api_versions) in nr.payload:
-        if name not in all_os:
-          # build a list of nodes for this os containing empty lists
-          # for each node in node_list
-          all_os[name] = {}
-          for nname in good_nodes:
-            all_os[name][nname] = []
-        # convert params from [name, help] to (name, help)
-        params = [tuple(v) for v in params]
-        all_os[name][node_name].append((path, status, diagnose,
-                                        variants, params, api_versions))
-    return all_os
-
-  def _GetQueryData(self, lu):
-    """Computes the list of nodes and their attributes.
-
-    """
-    # Locking is not used
-    assert not (compat.any(lu.glm.is_owned(level)
-                           for level in locking.LEVELS
-                           if level != locking.LEVEL_CLUSTER) or
-                self.do_locking or self.use_locking)
-
-    valid_nodes = [node.name
-                   for node in lu.cfg.GetAllNodesInfo().values()
-                   if not node.offline and node.vm_capable]
-    pol = self._DiagnoseByOS(lu.rpc.call_os_diagnose(valid_nodes))
-    cluster = lu.cfg.GetClusterInfo()
-
-    data = {}
-
-    for (os_name, os_data) in pol.items():
-      info = query.OsInfo(name=os_name, valid=True, node_status=os_data,
-                          hidden=(os_name in cluster.hidden_os),
-                          blacklisted=(os_name in cluster.blacklisted_os))
-
-      variants = set()
-      parameters = set()
-      api_versions = set()
-
-      for idx, osl in enumerate(os_data.values()):
-        info.valid = bool(info.valid and osl and osl[0][1])
-        if not info.valid:
-          break
-
-        (node_variants, node_params, node_api) = osl[0][3:6]
-        if idx == 0:
-          # First entry
-          variants.update(node_variants)
-          parameters.update(node_params)
-          api_versions.update(node_api)
-        else:
-          # Filter out inconsistent values
-          variants.intersection_update(node_variants)
-          parameters.intersection_update(node_params)
-          api_versions.intersection_update(node_api)
-
-      info.variants = list(variants)
-      info.parameters = list(parameters)
-      info.api_versions = list(api_versions)
-
-      data[os_name] = info
-
-    # Prepare data in requested order
-    return [data[name] for name in self._GetNames(lu, pol.keys(), None)
-            if name in data]
-
-
-class LUOsDiagnose(NoHooksLU):
-  """Logical unit for OS diagnose/query.
-
-  """
-  REQ_BGL = False
-
-  @staticmethod
-  def _BuildFilter(fields, names):
-    """Builds a filter for querying OSes.
-
-    """
-    name_filter = qlang.MakeSimpleFilter("name", names)
-
-    # Legacy behaviour: Hide hidden, blacklisted or invalid OSes if the
-    # respective field is not requested
-    status_filter = [[qlang.OP_NOT, [qlang.OP_TRUE, fname]]
-                     for fname in ["hidden", "blacklisted"]
-                     if fname not in fields]
-    if "valid" not in fields:
-      status_filter.append([qlang.OP_TRUE, "valid"])
-
-    if status_filter:
-      status_filter.insert(0, qlang.OP_AND)
-    else:
-      status_filter = None
-
-    if name_filter and status_filter:
-      return [qlang.OP_AND, name_filter, status_filter]
-    elif name_filter:
-      return name_filter
-    else:
-      return status_filter
-
-  def CheckArguments(self):
-    self.oq = _OsQuery(self._BuildFilter(self.op.output_fields, self.op.names),
-                       self.op.output_fields, False)
-
-  def ExpandNames(self):
-    self.oq.ExpandNames(self)
-
-  def Exec(self, feedback_fn):
-    return self.oq.OldStyleQuery(self)
-
-
 class _ExtStorageQuery(_QueryBase):
   FIELDS = query.EXTSTORAGE_FIELDS
 
diff --git a/lib/cmdlib/operating_system.py b/lib/cmdlib/operating_system.py
new file mode 100644 (file)
index 0000000..b6a4c13
--- /dev/null
@@ -0,0 +1,189 @@
+#
+#
+
+# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Google Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+
+
+"""Logical units dealing with OS."""
+
+from ganeti import compat
+from ganeti import locking
+from ganeti import qlang
+from ganeti import query
+from ganeti.cmdlib.base import NoHooksLU, _QueryBase
+
+
+class _OsQuery(_QueryBase):
+  FIELDS = query.OS_FIELDS
+
+  def ExpandNames(self, lu):
+    # Lock all nodes in shared mode
+    # Temporary removal of locks, should be reverted later
+    # TODO: reintroduce locks when they are lighter-weight
+    lu.needed_locks = {}
+    #self.share_locks[locking.LEVEL_NODE] = 1
+    #self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
+
+    # The following variables interact with _QueryBase._GetNames
+    if self.names:
+      self.wanted = self.names
+    else:
+      self.wanted = locking.ALL_SET
+
+    self.do_locking = self.use_locking
+
+  def DeclareLocks(self, lu, level):
+    pass
+
+  @staticmethod
+  def _DiagnoseByOS(rlist):
+    """Remaps a per-node return list into an a per-os per-node dictionary
+
+    @param rlist: a map with node names as keys and OS objects as values
+
+    @rtype: dict
+    @return: a dictionary with osnames as keys and as value another
+        map, with nodes as keys and tuples of (path, status, diagnose,
+        variants, parameters, api_versions) as values, eg::
+
+          {"debian-etch": {"node1": [(/usr/lib/..., True, "", [], []),
+                                     (/srv/..., False, "invalid api")],
+                           "node2": [(/srv/..., True, "", [], [])]}
+          }
+
+    """
+    all_os = {}
+    # we build here the list of nodes that didn't fail the RPC (at RPC
+    # level), so that nodes with a non-responding node daemon don't
+    # make all OSes invalid
+    good_nodes = [node_name for node_name in rlist
+                  if not rlist[node_name].fail_msg]
+    for node_name, nr in rlist.items():
+      if nr.fail_msg or not nr.payload:
+        continue
+      for (name, path, status, diagnose, variants,
+           params, api_versions) in nr.payload:
+        if name not in all_os:
+          # build a list of nodes for this os containing empty lists
+          # for each node in node_list
+          all_os[name] = {}
+          for nname in good_nodes:
+            all_os[name][nname] = []
+        # convert params from [name, help] to (name, help)
+        params = [tuple(v) for v in params]
+        all_os[name][node_name].append((path, status, diagnose,
+                                        variants, params, api_versions))
+    return all_os
+
+  def _GetQueryData(self, lu):
+    """Computes the list of nodes and their attributes.
+
+    """
+    # Locking is not used
+    assert not (compat.any(lu.glm.is_owned(level)
+                           for level in locking.LEVELS
+                           if level != locking.LEVEL_CLUSTER) or
+                self.do_locking or self.use_locking)
+
+    valid_nodes = [node.name
+                   for node in lu.cfg.GetAllNodesInfo().values()
+                   if not node.offline and node.vm_capable]
+    pol = self._DiagnoseByOS(lu.rpc.call_os_diagnose(valid_nodes))
+    cluster = lu.cfg.GetClusterInfo()
+
+    data = {}
+
+    for (os_name, os_data) in pol.items():
+      info = query.OsInfo(name=os_name, valid=True, node_status=os_data,
+                          hidden=(os_name in cluster.hidden_os),
+                          blacklisted=(os_name in cluster.blacklisted_os))
+
+      variants = set()
+      parameters = set()
+      api_versions = set()
+
+      for idx, osl in enumerate(os_data.values()):
+        info.valid = bool(info.valid and osl and osl[0][1])
+        if not info.valid:
+          break
+
+        (node_variants, node_params, node_api) = osl[0][3:6]
+        if idx == 0:
+          # First entry
+          variants.update(node_variants)
+          parameters.update(node_params)
+          api_versions.update(node_api)
+        else:
+          # Filter out inconsistent values
+          variants.intersection_update(node_variants)
+          parameters.intersection_update(node_params)
+          api_versions.intersection_update(node_api)
+
+      info.variants = list(variants)
+      info.parameters = list(parameters)
+      info.api_versions = list(api_versions)
+
+      data[os_name] = info
+
+    # Prepare data in requested order
+    return [data[name] for name in self._GetNames(lu, pol.keys(), None)
+            if name in data]
+
+
+class LUOsDiagnose(NoHooksLU):
+  """Logical unit for OS diagnose/query.
+
+  """
+  REQ_BGL = False
+
+  @staticmethod
+  def _BuildFilter(fields, names):
+    """Builds a filter for querying OSes.
+
+    """
+    name_filter = qlang.MakeSimpleFilter("name", names)
+
+    # Legacy behaviour: Hide hidden, blacklisted or invalid OSes if the
+    # respective field is not requested
+    status_filter = [[qlang.OP_NOT, [qlang.OP_TRUE, fname]]
+                     for fname in ["hidden", "blacklisted"]
+                     if fname not in fields]
+    if "valid" not in fields:
+      status_filter.append([qlang.OP_TRUE, "valid"])
+
+    if status_filter:
+      status_filter.insert(0, qlang.OP_AND)
+    else:
+      status_filter = None
+
+    if name_filter and status_filter:
+      return [qlang.OP_AND, name_filter, status_filter]
+    elif name_filter:
+      return name_filter
+    else:
+      return status_filter
+
+  def CheckArguments(self):
+    self.oq = _OsQuery(self._BuildFilter(self.op.output_fields, self.op.names),
+                       self.op.output_fields, False)
+
+  def ExpandNames(self):
+    self.oq.ExpandNames(self)
+
+  def Exec(self, feedback_fn):
+    return self.oq.OldStyleQuery(self)
index 16a6c29..9ffc4d8 100644 (file)
@@ -25,7 +25,6 @@ from ganeti import constants
 from ganeti import errors
 from ganeti import query
 from ganeti.cmdlib import _ExtStorageQuery
-from ganeti.cmdlib import _OsQuery
 from ganeti.cmdlib.backup import _ExportQuery
 from ganeti.cmdlib.base import NoHooksLU
 from ganeti.cmdlib.cluster import _ClusterQuery
@@ -33,6 +32,7 @@ from ganeti.cmdlib.group import _GroupQuery
 from ganeti.cmdlib.instance import _InstanceQuery
 from ganeti.cmdlib.network import _NetworkQuery
 from ganeti.cmdlib.node import _NodeQuery
+from ganeti.cmdlib.operating_system import _OsQuery
 
 
 #: Query type implementations