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=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)