4 # Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Google Inc.
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.
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.
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
22 """Logical units dealing with OS."""
24 from ganeti import compat
25 from ganeti import locking
26 from ganeti import qlang
27 from ganeti import query
28 from ganeti.cmdlib.base import NoHooksLU, _QueryBase
31 class _OsQuery(_QueryBase):
32 FIELDS = query.OS_FIELDS
34 def ExpandNames(self, lu):
35 # Lock all nodes in shared mode
36 # Temporary removal of locks, should be reverted later
37 # TODO: reintroduce locks when they are lighter-weight
39 #self.share_locks[locking.LEVEL_NODE] = 1
40 #self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
42 # The following variables interact with _QueryBase._GetNames
44 self.wanted = self.names
46 self.wanted = locking.ALL_SET
48 self.do_locking = self.use_locking
50 def DeclareLocks(self, lu, level):
54 def _DiagnoseByOS(rlist):
55 """Remaps a per-node return list into an a per-os per-node dictionary
57 @param rlist: a map with node names as keys and OS objects as values
60 @return: a dictionary with osnames as keys and as value another
61 map, with nodes as keys and tuples of (path, status, diagnose,
62 variants, parameters, api_versions) as values, eg::
64 {"debian-etch": {"node1": [(/usr/lib/..., True, "", [], []),
65 (/srv/..., False, "invalid api")],
66 "node2": [(/srv/..., True, "", [], [])]}
71 # we build here the list of nodes that didn't fail the RPC (at RPC
72 # level), so that nodes with a non-responding node daemon don't
73 # make all OSes invalid
74 good_nodes = [node_name for node_name in rlist
75 if not rlist[node_name].fail_msg]
76 for node_name, nr in rlist.items():
77 if nr.fail_msg or not nr.payload:
79 for (name, path, status, diagnose, variants,
80 params, api_versions) in nr.payload:
81 if name not in all_os:
82 # build a list of nodes for this os containing empty lists
83 # for each node in node_list
85 for nname in good_nodes:
86 all_os[name][nname] = []
87 # convert params from [name, help] to (name, help)
88 params = [tuple(v) for v in params]
89 all_os[name][node_name].append((path, status, diagnose,
90 variants, params, api_versions))
93 def _GetQueryData(self, lu):
94 """Computes the list of nodes and their attributes.
98 assert not (compat.any(lu.glm.is_owned(level)
99 for level in locking.LEVELS
100 if level != locking.LEVEL_CLUSTER) or
101 self.do_locking or self.use_locking)
103 valid_nodes = [node.name
104 for node in lu.cfg.GetAllNodesInfo().values()
105 if not node.offline and node.vm_capable]
106 pol = self._DiagnoseByOS(lu.rpc.call_os_diagnose(valid_nodes))
107 cluster = lu.cfg.GetClusterInfo()
111 for (os_name, os_data) in pol.items():
112 info = query.OsInfo(name=os_name, valid=True, node_status=os_data,
113 hidden=(os_name in cluster.hidden_os),
114 blacklisted=(os_name in cluster.blacklisted_os))
120 for idx, osl in enumerate(os_data.values()):
121 info.valid = bool(info.valid and osl and osl[0][1])
125 (node_variants, node_params, node_api) = osl[0][3:6]
128 variants.update(node_variants)
129 parameters.update(node_params)
130 api_versions.update(node_api)
132 # Filter out inconsistent values
133 variants.intersection_update(node_variants)
134 parameters.intersection_update(node_params)
135 api_versions.intersection_update(node_api)
137 info.variants = list(variants)
138 info.parameters = list(parameters)
139 info.api_versions = list(api_versions)
143 # Prepare data in requested order
144 return [data[name] for name in self._GetNames(lu, pol.keys(), None)
148 class LUOsDiagnose(NoHooksLU):
149 """Logical unit for OS diagnose/query.
155 def _BuildFilter(fields, names):
156 """Builds a filter for querying OSes.
159 name_filter = qlang.MakeSimpleFilter("name", names)
161 # Legacy behaviour: Hide hidden, blacklisted or invalid OSes if the
162 # respective field is not requested
163 status_filter = [[qlang.OP_NOT, [qlang.OP_TRUE, fname]]
164 for fname in ["hidden", "blacklisted"]
165 if fname not in fields]
166 if "valid" not in fields:
167 status_filter.append([qlang.OP_TRUE, "valid"])
170 status_filter.insert(0, qlang.OP_AND)
174 if name_filter and status_filter:
175 return [qlang.OP_AND, name_filter, status_filter]
181 def CheckArguments(self):
182 self.oq = _OsQuery(self._BuildFilter(self.op.output_fields, self.op.names),
183 self.op.output_fields, False)
185 def ExpandNames(self):
186 self.oq.ExpandNames(self)
188 def Exec(self, feedback_fn):
189 return self.oq.OldStyleQuery(self)