root / lib / cmdlib / operating_system.py @ 1be6b00e
History | View | Annotate | Download (6.1 kB)
1 |
#
|
---|---|
2 |
#
|
3 |
|
4 |
# Copyright (C) 2006, 2007, 2008, 2009, 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 |
"""Logical units dealing with OS."""
|
23 |
|
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 |
29 |
|
30 |
|
31 |
class _OsQuery(_QueryBase): |
32 |
FIELDS = query.OS_FIELDS |
33 |
|
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
|
38 |
lu.needed_locks = {} |
39 |
#self.share_locks[locking.LEVEL_NODE] = 1
|
40 |
#self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
|
41 |
|
42 |
# The following variables interact with _QueryBase._GetNames
|
43 |
if self.names: |
44 |
self.wanted = self.names |
45 |
else:
|
46 |
self.wanted = locking.ALL_SET
|
47 |
|
48 |
self.do_locking = self.use_locking |
49 |
|
50 |
def DeclareLocks(self, lu, level): |
51 |
pass
|
52 |
|
53 |
@staticmethod
|
54 |
def _DiagnoseByOS(rlist): |
55 |
"""Remaps a per-node return list into an a per-os per-node dictionary
|
56 |
|
57 |
@param rlist: a map with node names as keys and OS objects as values
|
58 |
|
59 |
@rtype: dict
|
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::
|
63 |
|
64 |
{"debian-etch": {"node1": [(/usr/lib/..., True, "", [], []),
|
65 |
(/srv/..., False, "invalid api")],
|
66 |
"node2": [(/srv/..., True, "", [], [])]}
|
67 |
}
|
68 |
|
69 |
"""
|
70 |
all_os = {} |
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: |
78 |
continue
|
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
|
84 |
all_os[name] = {} |
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)) |
91 |
return all_os
|
92 |
|
93 |
def _GetQueryData(self, lu): |
94 |
"""Computes the list of nodes and their attributes.
|
95 |
|
96 |
"""
|
97 |
# Locking is not used
|
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) |
102 |
|
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() |
108 |
|
109 |
data = {} |
110 |
|
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))
|
115 |
|
116 |
variants = set()
|
117 |
parameters = set()
|
118 |
api_versions = set()
|
119 |
|
120 |
for idx, osl in enumerate(os_data.values()): |
121 |
info.valid = bool(info.valid and osl and osl[0][1]) |
122 |
if not info.valid: |
123 |
break
|
124 |
|
125 |
(node_variants, node_params, node_api) = osl[0][3:6] |
126 |
if idx == 0: |
127 |
# First entry
|
128 |
variants.update(node_variants) |
129 |
parameters.update(node_params) |
130 |
api_versions.update(node_api) |
131 |
else:
|
132 |
# Filter out inconsistent values
|
133 |
variants.intersection_update(node_variants) |
134 |
parameters.intersection_update(node_params) |
135 |
api_versions.intersection_update(node_api) |
136 |
|
137 |
info.variants = list(variants)
|
138 |
info.parameters = list(parameters)
|
139 |
info.api_versions = list(api_versions)
|
140 |
|
141 |
data[os_name] = info |
142 |
|
143 |
# Prepare data in requested order
|
144 |
return [data[name] for name in self._GetNames(lu, pol.keys(), None) |
145 |
if name in data] |
146 |
|
147 |
|
148 |
class LUOsDiagnose(NoHooksLU): |
149 |
"""Logical unit for OS diagnose/query.
|
150 |
|
151 |
"""
|
152 |
REQ_BGL = False
|
153 |
|
154 |
@staticmethod
|
155 |
def _BuildFilter(fields, names): |
156 |
"""Builds a filter for querying OSes.
|
157 |
|
158 |
"""
|
159 |
name_filter = qlang.MakeSimpleFilter("name", names)
|
160 |
|
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"])
|
168 |
|
169 |
if status_filter:
|
170 |
status_filter.insert(0, qlang.OP_AND)
|
171 |
else:
|
172 |
status_filter = None
|
173 |
|
174 |
if name_filter and status_filter: |
175 |
return [qlang.OP_AND, name_filter, status_filter]
|
176 |
elif name_filter:
|
177 |
return name_filter
|
178 |
else:
|
179 |
return status_filter
|
180 |
|
181 |
def CheckArguments(self): |
182 |
self.oq = _OsQuery(self._BuildFilter(self.op.output_fields, self.op.names), |
183 |
self.op.output_fields, False) |
184 |
|
185 |
def ExpandNames(self): |
186 |
self.oq.ExpandNames(self) |
187 |
|
188 |
def Exec(self, feedback_fn): |
189 |
return self.oq.OldStyleQuery(self) |