Revision 1be6b00e

b/Makefile.am
318 318
	lib/cmdlib/instance_utils.py \
319 319
	lib/cmdlib/backup.py \
320 320
	lib/cmdlib/query.py \
321
	lib/cmdlib/operating_system.py \
321 322
	lib/cmdlib/tags.py \
322 323
	lib/cmdlib/network.py \
323 324
	lib/cmdlib/test.py
b/lib/cmdlib/__init__.py
82 82
from ganeti.cmdlib.backup import LUBackupQuery, LUBackupPrepare, \
83 83
  LUBackupExport, LUBackupRemove
84 84
from ganeti.cmdlib.query import LUQuery, LUQueryFields
85
from ganeti.cmdlib.operating_system import LUOsDiagnose
85 86
from ganeti.cmdlib.tags import LUTagsGet, LUTagsSearch, LUTagsSet, LUTagsDel
86 87
from ganeti.cmdlib.network import LUNetworkAdd, LUNetworkRemove, \
87 88
  LUNetworkSetParams, LUNetworkQuery, LUNetworkConnect, LUNetworkDisconnect
......
274 275
                               utils.CommaJoin(errs))
275 276

  
276 277

  
277
class _OsQuery(_QueryBase):
278
  FIELDS = query.OS_FIELDS
279

  
280
  def ExpandNames(self, lu):
281
    # Lock all nodes in shared mode
282
    # Temporary removal of locks, should be reverted later
283
    # TODO: reintroduce locks when they are lighter-weight
284
    lu.needed_locks = {}
285
    #self.share_locks[locking.LEVEL_NODE] = 1
286
    #self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
287

  
288
    # The following variables interact with _QueryBase._GetNames
289
    if self.names:
290
      self.wanted = self.names
291
    else:
292
      self.wanted = locking.ALL_SET
293

  
294
    self.do_locking = self.use_locking
295

  
296
  def DeclareLocks(self, lu, level):
297
    pass
298

  
299
  @staticmethod
300
  def _DiagnoseByOS(rlist):
301
    """Remaps a per-node return list into an a per-os per-node dictionary
302

  
303
    @param rlist: a map with node names as keys and OS objects as values
304

  
305
    @rtype: dict
306
    @return: a dictionary with osnames as keys and as value another
307
        map, with nodes as keys and tuples of (path, status, diagnose,
308
        variants, parameters, api_versions) as values, eg::
309

  
310
          {"debian-etch": {"node1": [(/usr/lib/..., True, "", [], []),
311
                                     (/srv/..., False, "invalid api")],
312
                           "node2": [(/srv/..., True, "", [], [])]}
313
          }
314

  
315
    """
316
    all_os = {}
317
    # we build here the list of nodes that didn't fail the RPC (at RPC
318
    # level), so that nodes with a non-responding node daemon don't
319
    # make all OSes invalid
320
    good_nodes = [node_name for node_name in rlist
321
                  if not rlist[node_name].fail_msg]
322
    for node_name, nr in rlist.items():
323
      if nr.fail_msg or not nr.payload:
324
        continue
325
      for (name, path, status, diagnose, variants,
326
           params, api_versions) in nr.payload:
327
        if name not in all_os:
328
          # build a list of nodes for this os containing empty lists
329
          # for each node in node_list
330
          all_os[name] = {}
331
          for nname in good_nodes:
332
            all_os[name][nname] = []
333
        # convert params from [name, help] to (name, help)
334
        params = [tuple(v) for v in params]
335
        all_os[name][node_name].append((path, status, diagnose,
336
                                        variants, params, api_versions))
337
    return all_os
338

  
339
  def _GetQueryData(self, lu):
340
    """Computes the list of nodes and their attributes.
341

  
342
    """
343
    # Locking is not used
344
    assert not (compat.any(lu.glm.is_owned(level)
345
                           for level in locking.LEVELS
346
                           if level != locking.LEVEL_CLUSTER) or
347
                self.do_locking or self.use_locking)
348

  
349
    valid_nodes = [node.name
350
                   for node in lu.cfg.GetAllNodesInfo().values()
351
                   if not node.offline and node.vm_capable]
352
    pol = self._DiagnoseByOS(lu.rpc.call_os_diagnose(valid_nodes))
353
    cluster = lu.cfg.GetClusterInfo()
354

  
355
    data = {}
356

  
357
    for (os_name, os_data) in pol.items():
358
      info = query.OsInfo(name=os_name, valid=True, node_status=os_data,
359
                          hidden=(os_name in cluster.hidden_os),
360
                          blacklisted=(os_name in cluster.blacklisted_os))
361

  
362
      variants = set()
363
      parameters = set()
364
      api_versions = set()
365

  
366
      for idx, osl in enumerate(os_data.values()):
367
        info.valid = bool(info.valid and osl and osl[0][1])
368
        if not info.valid:
369
          break
370

  
371
        (node_variants, node_params, node_api) = osl[0][3:6]
372
        if idx == 0:
373
          # First entry
374
          variants.update(node_variants)
375
          parameters.update(node_params)
376
          api_versions.update(node_api)
377
        else:
378
          # Filter out inconsistent values
379
          variants.intersection_update(node_variants)
380
          parameters.intersection_update(node_params)
381
          api_versions.intersection_update(node_api)
382

  
383
      info.variants = list(variants)
384
      info.parameters = list(parameters)
385
      info.api_versions = list(api_versions)
386

  
387
      data[os_name] = info
388

  
389
    # Prepare data in requested order
390
    return [data[name] for name in self._GetNames(lu, pol.keys(), None)
391
            if name in data]
392

  
393

  
394
class LUOsDiagnose(NoHooksLU):
395
  """Logical unit for OS diagnose/query.
396

  
397
  """
398
  REQ_BGL = False
399

  
400
  @staticmethod
401
  def _BuildFilter(fields, names):
402
    """Builds a filter for querying OSes.
403

  
404
    """
405
    name_filter = qlang.MakeSimpleFilter("name", names)
406

  
407
    # Legacy behaviour: Hide hidden, blacklisted or invalid OSes if the
408
    # respective field is not requested
409
    status_filter = [[qlang.OP_NOT, [qlang.OP_TRUE, fname]]
410
                     for fname in ["hidden", "blacklisted"]
411
                     if fname not in fields]
412
    if "valid" not in fields:
413
      status_filter.append([qlang.OP_TRUE, "valid"])
414

  
415
    if status_filter:
416
      status_filter.insert(0, qlang.OP_AND)
417
    else:
418
      status_filter = None
419

  
420
    if name_filter and status_filter:
421
      return [qlang.OP_AND, name_filter, status_filter]
422
    elif name_filter:
423
      return name_filter
424
    else:
425
      return status_filter
426

  
427
  def CheckArguments(self):
428
    self.oq = _OsQuery(self._BuildFilter(self.op.output_fields, self.op.names),
429
                       self.op.output_fields, False)
430

  
431
  def ExpandNames(self):
432
    self.oq.ExpandNames(self)
433

  
434
  def Exec(self, feedback_fn):
435
    return self.oq.OldStyleQuery(self)
436

  
437

  
438 278
class _ExtStorageQuery(_QueryBase):
439 279
  FIELDS = query.EXTSTORAGE_FIELDS
440 280

  
b/lib/cmdlib/operating_system.py
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)
b/lib/cmdlib/query.py
25 25
from ganeti import errors
26 26
from ganeti import query
27 27
from ganeti.cmdlib import _ExtStorageQuery
28
from ganeti.cmdlib import _OsQuery
29 28
from ganeti.cmdlib.backup import _ExportQuery
30 29
from ganeti.cmdlib.base import NoHooksLU
31 30
from ganeti.cmdlib.cluster import _ClusterQuery
......
33 32
from ganeti.cmdlib.instance import _InstanceQuery
34 33
from ganeti.cmdlib.network import _NetworkQuery
35 34
from ganeti.cmdlib.node import _NodeQuery
35
from ganeti.cmdlib.operating_system import _OsQuery
36 36

  
37 37

  
38 38
#: Query type implementations

Also available in: Unified diff