gnt_os.py 8.24 KB
Newer Older
1
#
Iustin Pop's avatar
Iustin Pop committed
2
3
#

4
# Copyright (C) 2006, 2007, 2010 Google Inc.
Iustin Pop's avatar
Iustin Pop committed
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.

Iustin Pop's avatar
Iustin Pop committed
21
"""OS scripts related commands"""
Iustin Pop's avatar
Iustin Pop committed
22

23
# pylint: disable-msg=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)
Iustin Pop's avatar
Iustin Pop committed
27
# C0103: Invalid name gnt-os
28

Iustin Pop's avatar
Iustin Pop committed
29
from ganeti.cli import *
Iustin Pop's avatar
Iustin Pop committed
30
from ganeti import constants
Iustin Pop's avatar
Iustin Pop committed
31
32
from ganeti import opcodes
from ganeti import utils
Guido Trotter's avatar
Guido Trotter committed
33

34

Iustin Pop's avatar
Iustin Pop committed
35
def ListOS(opts, args):
36
37
38
39
40
41
42
  """List the valid OSes in the cluster.

  @param opts: the command line options selected by the user
  @type args: list
  @param args: should be an empty list
  @rtype: int
  @return: the desired exit code
Iustin Pop's avatar
Iustin Pop committed
43
44

  """
45
  op = opcodes.OpOsDiagnose(output_fields=["name", "variants"], names=[])
46
  result = SubmitOpCode(op, opts=opts)
Iustin Pop's avatar
Iustin Pop committed
47
48

  if not result:
49
    ToStderr("Can't get the OS list")
Iustin Pop's avatar
Iustin Pop committed
50
51
52
    return 1

  if not opts.no_headers:
53
54
55
    headers = {"name": "Name"}
  else:
    headers = None
Iustin Pop's avatar
Iustin Pop committed
56

57
  os_names = []
58
59
  for (name, variants) in result:
    os_names.extend([[n] for n in CalculateOSNames(name, variants)])
60

61
  data = GenerateTable(separator=None, headers=headers, fields=["name"],
62
                       data=os_names, units=None)
63
64

  for line in data:
65
    ToStdout(line)
Iustin Pop's avatar
Iustin Pop committed
66
67
68

  return 0

Guido Trotter's avatar
Guido Trotter committed
69

Iustin Pop's avatar
Iustin Pop committed
70
71
72
73
74
75
76
77
78
79
def ShowOSInfo(opts, args):
  """List detailed information about OSes in the cluster.

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

  """
80
  op = opcodes.OpOsDiagnose(output_fields=["name", "valid", "variants",
81
82
                                           "parameters", "api_versions",
                                           "blacklisted", "hidden"],
Iustin Pop's avatar
Iustin Pop committed
83
84
85
86
87
88
89
90
91
                            names=[])
  result = SubmitOpCode(op, opts=opts)

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

  do_filter = bool(args)

92
  for (name, valid, variants, parameters, api_versions, blk, hid) in result:
Iustin Pop's avatar
Iustin Pop committed
93
94
95
96
97
98
99
    if do_filter:
      if name not in args:
        continue
      else:
        args.remove(name)
    ToStdout("%s:", name)
    ToStdout("  - valid: %s", valid)
100
101
    ToStdout("  - hidden: %s", hid)
    ToStdout("  - blacklisted: %s", blk)
Iustin Pop's avatar
Iustin Pop committed
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
    if valid:
      ToStdout("  - API versions:")
      for version in sorted(api_versions):
        ToStdout("    - %s", version)
      ToStdout("  - variants:")
      for vname in variants:
        ToStdout("    - %s", vname)
      ToStdout("  - parameters:")
      for pname, pdesc in parameters:
        ToStdout("    - %s: %s", pname, pdesc)
    ToStdout("")

  if args:
    for name in args:
      ToStdout("%s: ", name)
      ToStdout("")

  return 0


122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
def _OsStatus(status, diagnose):
  """Beautifier function for OS status.

  @type status: boolean
  @param status: is the OS valid
  @type diagnose: string
  @param diagnose: the error message for invalid OSes
  @rtype: string
  @return: a formatted status

  """
  if status:
    return "valid"
  else:
    return "invalid - %s" % diagnose

138

Iustin Pop's avatar
Iustin Pop committed
139
140
141
def DiagnoseOS(opts, args):
  """Analyse all OSes on this cluster.

142
143
144
145
146
147
  @param opts: the command line options selected by the user
  @type args: list
  @param args: should be an empty list
  @rtype: int
  @return: the desired exit code

Iustin Pop's avatar
Iustin Pop committed
148
  """
149
  op = opcodes.OpOsDiagnose(output_fields=["name", "valid", "variants",
150
151
                                           "node_status", "hidden",
                                           "blacklisted"], names=[])
152
  result = SubmitOpCode(op, opts=opts)
Iustin Pop's avatar
Iustin Pop committed
153
154

  if not result:
155
    ToStderr("Can't get the OS list")
Iustin Pop's avatar
Iustin Pop committed
156
157
    return 1

158
159
  has_bad = False

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

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

198
    # TODO: Shouldn't the global status be calculated by the LU?
Iustin Pop's avatar
Iustin Pop committed
199
200
201
202
    if nodes_valid and not nodes_bad:
      status = "valid"
    elif not nodes_valid and nodes_bad:
      status = "invalid"
203
      has_bad = True
Iustin Pop's avatar
Iustin Pop committed
204
205
    else:
      status = "partial valid"
206
      has_bad = True
207

208
209
    def _OutputPerNodeOSStatus(msg_map):
      map_k = utils.NiceSort(msg_map.keys())
Guido Trotter's avatar
Guido Trotter committed
210
      for node_name in map_k:
211
        ToStdout("  Node: %s, status: %s", node_name, msg_map[node_name])
212
        for msg in nodes_hidden[node_name]:
213
          ToStdout(msg)
Guido Trotter's avatar
Guido Trotter committed
214

215
216
217
218
219
220
    st_msg = "OS: %s [global status: %s]" % (os_name, status)
    if hid:
      st_msg += " [hidden]"
    if blk:
      st_msg += " [blacklisted]"
    ToStdout(st_msg)
221
    if os_variants:
222
      ToStdout("  Variants: [%s]" % utils.CommaJoin(os_variants))
Guido Trotter's avatar
Guido Trotter committed
223
224
    _OutputPerNodeOSStatus(nodes_valid)
    _OutputPerNodeOSStatus(nodes_bad)
225
    ToStdout("")
Iustin Pop's avatar
Iustin Pop committed
226

227
228
  return int(has_bad)

Iustin Pop's avatar
Iustin Pop committed
229

230
231
232
233
234
235
236
237
238
239
240
241
def ModifyOS(opts, args):
  """Modify OS parameters for one OS.

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

  """
  os = args[0]

242
243
244
245
246
247
248
249
250
251
  if opts.hvparams:
    os_hvp = {os: dict(opts.hvparams)}
  else:
    os_hvp = None

  if opts.osparams:
    osp = {os: opts.osparams}
  else:
    osp = None

252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
  if opts.hidden is not None:
    if opts.hidden:
      ohid = [(constants.DDM_ADD, os)]
    else:
      ohid = [(constants.DDM_REMOVE, os)]
  else:
    ohid = None

  if opts.blacklisted is not None:
    if opts.blacklisted:
      oblk = [(constants.DDM_ADD, os)]
    else:
      oblk = [(constants.DDM_REMOVE, os)]
  else:
    oblk = None

  if not (os_hvp or osp or ohid or oblk):
269
270
271
272
    ToStderr("At least one of OS parameters or hypervisor parameters"
             " must be passed")
    return 1

273
  op = opcodes.OpClusterSetParams(os_hvp=os_hvp,
274
                                  osparams=osp,
275
276
                                  hidden_os=ohid,
                                  blacklisted_os=oblk)
277
  SubmitOpCode(op, opts=opts)
278
279
280
281

  return 0


Iustin Pop's avatar
Iustin Pop committed
282
commands = {
Iustin Pop's avatar
Iustin Pop committed
283
  "list": (
284
285
    ListOS, ARGS_NONE, [NOHDR_OPT, PRIORITY_OPT],
    "", "Lists all valid operating systems on the cluster"),
Iustin Pop's avatar
Iustin Pop committed
286
  "diagnose": (
287
288
    DiagnoseOS, ARGS_NONE, [PRIORITY_OPT],
    "", "Diagnose all operating systems"),
Iustin Pop's avatar
Iustin Pop committed
289
  "info": (
290
291
    ShowOSInfo, [ArgOs()], [PRIORITY_OPT],
    "", "Show detailed information about "
Iustin Pop's avatar
Iustin Pop committed
292
    "operating systems"),
Iustin Pop's avatar
Iustin Pop committed
293
  "modify": (
294
    ModifyOS, ARGS_ONE_OS,
Iustin Pop's avatar
Iustin Pop committed
295
296
    [HVLIST_OPT, OSPARAMS_OPT, DRY_RUN_OPT, PRIORITY_OPT,
     HID_OS_OPT, BLK_OS_OPT],
297
    "", "Modify the OS parameters"),
Iustin Pop's avatar
Iustin Pop committed
298
299
  }

300
301
302

def Main():
  return GenericMain(commands)