gnt_os.py 8.18 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.OpDiagnoseOS(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
80
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

  """
  op = opcodes.OpDiagnoseOS(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.OpDiagnoseOS(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
199
200
201

    if nodes_valid and not nodes_bad:
      status = "valid"
    elif not nodes_valid and nodes_bad:
      status = "invalid"
202
      has_bad = True
Iustin Pop's avatar
Iustin Pop committed
203
204
    else:
      status = "partial valid"
205
      has_bad = True
206

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

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

226
227
  return int(has_bad)

Iustin Pop's avatar
Iustin Pop committed
228

229
230
231
232
233
234
235
236
237
238
239
240
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]

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

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

251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
  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):
268
269
270
271
    ToStderr("At least one of OS parameters or hypervisor parameters"
             " must be passed")
    return 1

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

  return 0


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

299
300
301

def Main():
  return GenericMain(commands)