Statistics
| Branch: | Tag: | Revision:

root / lib / client / gnt_os.py @ 82b948e4

History | View | Annotate | Download (8.3 kB)

1
#
2
#
3

    
4
# Copyright (C) 2006, 2007, 2010, 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
"""OS scripts related commands"""
22

    
23
# pylint: disable=W0401,W0613,W0614,C0103
24
# W0401: Wildcard import ganeti.cli
25
# W0613: Unused argument, since all functions follow the same API
26
# W0614: Unused import %s from wildcard import (since we need cli)
27
# C0103: Invalid name gnt-os
28

    
29
from ganeti.cli import *
30
from ganeti import constants
31
from ganeti import opcodes
32
from ganeti import utils
33

    
34

    
35
def ListOS(opts, args):
36
  """List the valid OSes in the cluster.
37

38
  @param opts: the command line options selected by the user
39
  @type args: list
40
  @param args: should be an empty list
41
  @rtype: int
42
  @return: the desired exit code
43

44
  """
45
  op = opcodes.OpOsDiagnose(output_fields=["name", "variants"], names=[])
46
  result = SubmitOpCode(op, opts=opts)
47

    
48
  if not opts.no_headers:
49
    headers = {"name": "Name"}
50
  else:
51
    headers = None
52

    
53
  os_names = []
54
  for (name, variants) in result:
55
    os_names.extend([[n] for n in CalculateOSNames(name, variants)])
56

    
57
  data = GenerateTable(separator=None, headers=headers, fields=["name"],
58
                       data=os_names, units=None)
59

    
60
  for line in data:
61
    ToStdout(line)
62

    
63
  return 0
64

    
65

    
66
def ShowOSInfo(opts, args):
67
  """List detailed information about OSes in the cluster.
68

69
  @param opts: the command line options selected by the user
70
  @type args: list
71
  @param args: should be an empty list
72
  @rtype: int
73
  @return: the desired exit code
74

75
  """
76
  op = opcodes.OpOsDiagnose(output_fields=["name", "valid", "variants",
77
                                           "parameters", "api_versions",
78
                                           "blacklisted", "hidden"],
79
                            names=[])
80
  result = SubmitOpCode(op, opts=opts)
81

    
82
  if not result:
83
    ToStderr("Can't get the OS list")
84
    return 1
85

    
86
  do_filter = bool(args)
87

    
88
  for (name, valid, variants, parameters, api_versions, blk, hid) in result:
89
    if do_filter:
90
      if name not in args:
91
        continue
92
      else:
93
        args.remove(name)
94
    ToStdout("%s:", name)
95
    ToStdout("  - valid: %s", valid)
96
    ToStdout("  - hidden: %s", hid)
97
    ToStdout("  - blacklisted: %s", blk)
98
    if valid:
99
      ToStdout("  - API versions:")
100
      for version in sorted(api_versions):
101
        ToStdout("    - %s", version)
102
      ToStdout("  - variants:")
103
      for vname in variants:
104
        ToStdout("    - %s", vname)
105
      ToStdout("  - parameters:")
106
      for pname, pdesc in parameters:
107
        ToStdout("    - %s: %s", pname, pdesc)
108
    ToStdout("")
109

    
110
  if args:
111
    for name in args:
112
      ToStdout("%s: ", name)
113
      ToStdout("")
114

    
115
  return 0
116

    
117

    
118
def _OsStatus(status, diagnose):
119
  """Beautifier function for OS status.
120

121
  @type status: boolean
122
  @param status: is the OS valid
123
  @type diagnose: string
124
  @param diagnose: the error message for invalid OSes
125
  @rtype: string
126
  @return: a formatted status
127

128
  """
129
  if status:
130
    return "valid"
131
  else:
132
    return "invalid - %s" % diagnose
133

    
134

    
135
def DiagnoseOS(opts, args):
136
  """Analyse all OSes on this cluster.
137

138
  @param opts: the command line options selected by the user
139
  @type args: list
140
  @param args: should be an empty list
141
  @rtype: int
142
  @return: the desired exit code
143

144
  """
145
  op = opcodes.OpOsDiagnose(output_fields=["name", "valid", "variants",
146
                                           "node_status", "hidden",
147
                                           "blacklisted"], names=[])
148
  result = SubmitOpCode(op, opts=opts)
149

    
150
  if not result:
151
    ToStderr("Can't get the OS list")
152
    return 1
153

    
154
  has_bad = False
155

    
156
  for os_name, _, os_variants, node_data, hid, blk in result:
157
    nodes_valid = {}
158
    nodes_bad = {}
159
    nodes_hidden = {}
160
    for node_name, node_info in node_data.iteritems():
161
      nodes_hidden[node_name] = []
162
      if node_info: # at least one entry in the per-node list
163
        (fo_path, fo_status, fo_msg, fo_variants,
164
         fo_params, fo_api) = node_info.pop(0)
165
        fo_msg = "%s (path: %s)" % (_OsStatus(fo_status, fo_msg), fo_path)
166
        if fo_api:
167
          max_os_api = max(fo_api)
168
          fo_msg += " [API versions: %s]" % utils.CommaJoin(fo_api)
169
        else:
170
          max_os_api = 0
171
          fo_msg += " [no API versions declared]"
172

    
173
        if max_os_api >= constants.OS_API_V15:
174
          if fo_variants:
175
            fo_msg += " [variants: %s]" % utils.CommaJoin(fo_variants)
176
          else:
177
            fo_msg += " [no variants]"
178
        if max_os_api >= constants.OS_API_V20:
179
          if fo_params:
180
            fo_msg += (" [parameters: %s]" %
181
                       utils.CommaJoin([v[0] for v in fo_params]))
182
          else:
183
            fo_msg += " [no parameters]"
184
        if fo_status:
185
          nodes_valid[node_name] = fo_msg
186
        else:
187
          nodes_bad[node_name] = fo_msg
188
        for hpath, hstatus, hmsg, _, _, _ in node_info:
189
          nodes_hidden[node_name].append("    [hidden] path: %s, status: %s" %
190
                                         (hpath, _OsStatus(hstatus, hmsg)))
191
      else:
192
        nodes_bad[node_name] = "OS not found"
193

    
194
    # TODO: Shouldn't the global status be calculated by the LU?
195
    if nodes_valid and not nodes_bad:
196
      status = "valid"
197
    elif not nodes_valid and nodes_bad:
198
      status = "invalid"
199
      has_bad = True
200
    else:
201
      status = "partial valid"
202
      has_bad = True
203

    
204
    def _OutputPerNodeOSStatus(msg_map):
205
      map_k = utils.NiceSort(msg_map.keys())
206
      for node_name in map_k:
207
        ToStdout("  Node: %s, status: %s", node_name, msg_map[node_name])
208
        for msg in nodes_hidden[node_name]:
209
          ToStdout(msg)
210

    
211
    st_msg = "OS: %s [global status: %s]" % (os_name, status)
212
    if hid:
213
      st_msg += " [hidden]"
214
    if blk:
215
      st_msg += " [blacklisted]"
216
    ToStdout(st_msg)
217
    if os_variants:
218
      ToStdout("  Variants: [%s]" % utils.CommaJoin(os_variants))
219
    _OutputPerNodeOSStatus(nodes_valid)
220
    _OutputPerNodeOSStatus(nodes_bad)
221
    ToStdout("")
222

    
223
  return int(has_bad)
224

    
225

    
226
def ModifyOS(opts, args):
227
  """Modify OS parameters for one OS.
228

229
  @param opts: the command line options selected by the user
230
  @type args: list
231
  @param args: should be a list with one entry
232
  @rtype: int
233
  @return: the desired exit code
234

235
  """
236
  os = args[0]
237

    
238
  if opts.hvparams:
239
    os_hvp = {os: dict(opts.hvparams)}
240
  else:
241
    os_hvp = None
242

    
243
  if opts.osparams:
244
    osp = {os: opts.osparams}
245
  else:
246
    osp = None
247

    
248
  if opts.hidden is not None:
249
    if opts.hidden:
250
      ohid = [(constants.DDM_ADD, os)]
251
    else:
252
      ohid = [(constants.DDM_REMOVE, os)]
253
  else:
254
    ohid = None
255

    
256
  if opts.blacklisted is not None:
257
    if opts.blacklisted:
258
      oblk = [(constants.DDM_ADD, os)]
259
    else:
260
      oblk = [(constants.DDM_REMOVE, os)]
261
  else:
262
    oblk = None
263

    
264
  if not (os_hvp or osp or ohid or oblk):
265
    ToStderr("At least one of OS parameters or hypervisor parameters"
266
             " must be passed")
267
    return 1
268

    
269
  op = opcodes.OpClusterSetParams(os_hvp=os_hvp,
270
                                  osparams=osp,
271
                                  hidden_os=ohid,
272
                                  blacklisted_os=oblk)
273
  SubmitOrSend(op, opts)
274

    
275
  return 0
276

    
277

    
278
commands = {
279
  "list": (
280
    ListOS, ARGS_NONE, [NOHDR_OPT, PRIORITY_OPT],
281
    "", "Lists all valid operating systems on the cluster"),
282
  "diagnose": (
283
    DiagnoseOS, ARGS_NONE, [PRIORITY_OPT],
284
    "", "Diagnose all operating systems"),
285
  "info": (
286
    ShowOSInfo, [ArgOs()], [PRIORITY_OPT],
287
    "", "Show detailed information about "
288
    "operating systems"),
289
  "modify": (
290
    ModifyOS, ARGS_ONE_OS,
291
    [HVLIST_OPT, OSPARAMS_OPT, DRY_RUN_OPT, PRIORITY_OPT,
292
     HID_OS_OPT, BLK_OS_OPT, SUBMIT_OPT],
293
    "", "Modify the OS parameters"),
294
  }
295

    
296
#: dictionary with aliases for commands
297
aliases = {
298
  "show": "info",
299
  }
300

    
301

    
302
def Main():
303
  return GenericMain(commands, aliases=aliases)