Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / operating_system.py @ 18397489

History | View | Annotate | Download (6.2 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 QueryBase, NoHooksLU
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 node UUIDs as keys and tuples of (path, status, diagnose,
62
        variants, parameters, api_versions) as values, eg::
63

64
          {"debian-etch": {"node1-uuid": [(/usr/lib/..., True, "", [], []),
65
                                          (/srv/..., False, "invalid api")],
66
                           "node2-uuid": [(/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_node_uuids = [node_uuid for node_uuid in rlist
75
                       if not rlist[node_uuid].fail_msg]
76
    for node_uuid, 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 nuuid in good_node_uuids:
86
            all_os[name][nuuid] = []
87
        # convert params from [name, help] to (name, help)
88
        params = [tuple(v) for v in params]
89
        all_os[name][node_uuid].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_node_uuids = [node.uuid
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_node_uuids))
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)