Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / operating_system.py @ 87ed6b79

History | View | Annotate | Download (5.9 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 locking
25
from ganeti import qlang
26
from ganeti import query
27
from ganeti.cmdlib.base import QueryBase, NoHooksLU
28

    
29

    
30
class OsQuery(QueryBase):
31
  FIELDS = query.OS_FIELDS
32

    
33
  def ExpandNames(self, lu):
34
    # Lock all nodes in shared mode
35
    # Temporary removal of locks, should be reverted later
36
    # TODO: reintroduce locks when they are lighter-weight
37
    lu.needed_locks = {}
38
    #self.share_locks[locking.LEVEL_NODE] = 1
39
    #self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
40

    
41
    # The following variables interact with _QueryBase._GetNames
42
    if self.names:
43
      self.wanted = self.names
44
    else:
45
      self.wanted = locking.ALL_SET
46

    
47
    self.do_locking = self.use_locking
48

    
49
  def DeclareLocks(self, lu, level):
50
    pass
51

    
52
  @staticmethod
53
  def _DiagnoseByOS(rlist):
54
    """Remaps a per-node return list into an a per-os per-node dictionary
55

56
    @param rlist: a map with node names as keys and OS objects as values
57

58
    @rtype: dict
59
    @return: a dictionary with osnames as keys and as value another
60
        map, with node UUIDs as keys and tuples of (path, status, diagnose,
61
        variants, parameters, api_versions) as values, eg::
62

63
          {"debian-etch": {"node1-uuid": [(/usr/lib/..., True, "", [], []),
64
                                          (/srv/..., False, "invalid api")],
65
                           "node2-uuid": [(/srv/..., True, "", [], [])]}
66
          }
67

68
    """
69
    all_os = {}
70
    # we build here the list of nodes that didn't fail the RPC (at RPC
71
    # level), so that nodes with a non-responding node daemon don't
72
    # make all OSes invalid
73
    good_node_uuids = [node_uuid for node_uuid in rlist
74
                       if not rlist[node_uuid].fail_msg]
75
    for node_uuid, nr in rlist.items():
76
      if nr.fail_msg or not nr.payload:
77
        continue
78
      for (name, path, status, diagnose, variants,
79
           params, api_versions) in nr.payload:
80
        if name not in all_os:
81
          # build a list of nodes for this os containing empty lists
82
          # for each node in node_list
83
          all_os[name] = {}
84
          for nuuid in good_node_uuids:
85
            all_os[name][nuuid] = []
86
        # convert params from [name, help] to (name, help)
87
        params = [tuple(v) for v in params]
88
        all_os[name][node_uuid].append((path, status, diagnose,
89
                                        variants, params, api_versions))
90
    return all_os
91

    
92
  def _GetQueryData(self, lu):
93
    """Computes the list of nodes and their attributes.
94

95
    """
96
    valid_node_uuids = [node.uuid
97
                        for node in lu.cfg.GetAllNodesInfo().values()
98
                        if not node.offline and node.vm_capable]
99
    pol = self._DiagnoseByOS(lu.rpc.call_os_diagnose(valid_node_uuids))
100
    cluster = lu.cfg.GetClusterInfo()
101

    
102
    data = {}
103

    
104
    for (os_name, os_data) in pol.items():
105
      info = query.OsInfo(name=os_name, valid=True, node_status=os_data,
106
                          hidden=(os_name in cluster.hidden_os),
107
                          blacklisted=(os_name in cluster.blacklisted_os))
108

    
109
      variants = set()
110
      parameters = set()
111
      api_versions = set()
112

    
113
      for idx, osl in enumerate(os_data.values()):
114
        info.valid = bool(info.valid and osl and osl[0][1])
115
        if not info.valid:
116
          break
117

    
118
        (node_variants, node_params, node_api) = osl[0][3:6]
119
        if idx == 0:
120
          # First entry
121
          variants.update(node_variants)
122
          parameters.update(node_params)
123
          api_versions.update(node_api)
124
        else:
125
          # Filter out inconsistent values
126
          variants.intersection_update(node_variants)
127
          parameters.intersection_update(node_params)
128
          api_versions.intersection_update(node_api)
129

    
130
      info.variants = list(variants)
131
      info.parameters = list(parameters)
132
      info.api_versions = list(api_versions)
133

    
134
      data[os_name] = info
135

    
136
    # Prepare data in requested order
137
    return [data[name] for name in self._GetNames(lu, pol.keys(), None)
138
            if name in data]
139

    
140

    
141
class LUOsDiagnose(NoHooksLU):
142
  """Logical unit for OS diagnose/query.
143

144
  """
145
  REQ_BGL = False
146

    
147
  @staticmethod
148
  def _BuildFilter(fields, names):
149
    """Builds a filter for querying OSes.
150

151
    """
152
    name_filter = qlang.MakeSimpleFilter("name", names)
153

    
154
    # Legacy behaviour: Hide hidden, blacklisted or invalid OSes if the
155
    # respective field is not requested
156
    status_filter = [[qlang.OP_NOT, [qlang.OP_TRUE, fname]]
157
                     for fname in ["hidden", "blacklisted"]
158
                     if fname not in fields]
159
    if "valid" not in fields:
160
      status_filter.append([qlang.OP_TRUE, "valid"])
161

    
162
    if status_filter:
163
      status_filter.insert(0, qlang.OP_AND)
164
    else:
165
      status_filter = None
166

    
167
    if name_filter and status_filter:
168
      return [qlang.OP_AND, name_filter, status_filter]
169
    elif name_filter:
170
      return name_filter
171
    else:
172
      return status_filter
173

    
174
  def CheckArguments(self):
175
    self.oq = OsQuery(self._BuildFilter(self.op.output_fields, self.op.names),
176
                       self.op.output_fields, False)
177

    
178
  def ExpandNames(self):
179
    self.oq.ExpandNames(self)
180

    
181
  def Exec(self, feedback_fn):
182
    return self.oq.OldStyleQuery(self)