cli: Expose priority option and pass priority to master daemon
[ganeti-local] / scripts / gnt-os
1 #!/usr/bin/python
2 #
3
4 # Copyright (C) 2006, 2007, 2010 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-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)
27 # C0103: Invalid name gnt-os
28
29 import sys
30
31 from ganeti.cli import *
32 from ganeti import constants
33 from ganeti import opcodes
34 from ganeti import utils
35
36
37 def ListOS(opts, args):
38   """List the valid OSes in the cluster.
39
40   @param opts: the command line options selected by the user
41   @type args: list
42   @param args: should be an empty list
43   @rtype: int
44   @return: the desired exit code
45
46   """
47   op = opcodes.OpDiagnoseOS(output_fields=["name", "valid", "variants"],
48                             names=[])
49   result = SubmitOpCode(op, opts=opts)
50
51   if not result:
52     ToStderr("Can't get the OS list")
53     return 1
54
55   if not opts.no_headers:
56     headers = {"name": "Name"}
57   else:
58     headers = None
59
60   os_names = []
61   for (name, valid, variants) in result:
62     if valid:
63       os_names.extend([[n] for n in CalculateOSNames(name, variants)])
64
65   data = GenerateTable(separator=None, headers=headers, fields=["name"],
66                        data=os_names, units=None)
67
68   for line in data:
69     ToStdout(line)
70
71   return 0
72
73
74 def ShowOSInfo(opts, args):
75   """List detailed information about OSes in the cluster.
76
77   @param opts: the command line options selected by the user
78   @type args: list
79   @param args: should be an empty list
80   @rtype: int
81   @return: the desired exit code
82
83   """
84   op = opcodes.OpDiagnoseOS(output_fields=["name", "valid", "variants",
85                                            "parameters", "api_versions"],
86                             names=[])
87   result = SubmitOpCode(op, opts=opts)
88
89   if not result:
90     ToStderr("Can't get the OS list")
91     return 1
92
93   do_filter = bool(args)
94
95   for (name, valid, variants, parameters, api_versions) in result:
96     if do_filter:
97       if name not in args:
98         continue
99       else:
100         args.remove(name)
101     ToStdout("%s:", name)
102     ToStdout("  - valid: %s", valid)
103     if valid:
104       ToStdout("  - API versions:")
105       for version in sorted(api_versions):
106         ToStdout("    - %s", version)
107       ToStdout("  - variants:")
108       for vname in variants:
109         ToStdout("    - %s", vname)
110       ToStdout("  - parameters:")
111       for pname, pdesc in parameters:
112         ToStdout("    - %s: %s", pname, pdesc)
113     ToStdout("")
114
115   if args:
116     for name in args:
117       ToStdout("%s: ", name)
118       ToStdout("")
119
120   return 0
121
122
123 def _OsStatus(status, diagnose):
124   """Beautifier function for OS status.
125
126   @type status: boolean
127   @param status: is the OS valid
128   @type diagnose: string
129   @param diagnose: the error message for invalid OSes
130   @rtype: string
131   @return: a formatted status
132
133   """
134   if status:
135     return "valid"
136   else:
137     return "invalid - %s" % diagnose
138
139 def DiagnoseOS(opts, args):
140   """Analyse all OSes on this cluster.
141
142   @param opts: the command line options selected by the user
143   @type args: list
144   @param args: should be an empty list
145   @rtype: int
146   @return: the desired exit code
147
148   """
149   op = opcodes.OpDiagnoseOS(output_fields=["name", "valid", "variants",
150                                            "node_status"], names=[])
151   result = SubmitOpCode(op, opts=opts)
152
153   if not result:
154     ToStderr("Can't get the OS list")
155     return 1
156
157   has_bad = False
158
159   for os_name, _, os_variants, node_data in result:
160     nodes_valid = {}
161     nodes_bad = {}
162     nodes_hidden = {}
163     for node_name, node_info in node_data.iteritems():
164       nodes_hidden[node_name] = []
165       if node_info: # at least one entry in the per-node list
166         (fo_path, fo_status, fo_msg, fo_variants,
167          fo_params, fo_api) = node_info.pop(0)
168         fo_msg = "%s (path: %s)" % (_OsStatus(fo_status, fo_msg), fo_path)
169         if fo_api:
170           max_os_api = max(fo_api)
171           fo_msg += " [API versions: %s]" % utils.CommaJoin(fo_api)
172         else:
173           max_os_api = 0
174           fo_msg += " [no API versions declared]"
175         if max_os_api >= constants.OS_API_V15:
176           if fo_variants:
177             fo_msg += " [variants: %s]" % utils.CommaJoin(fo_variants)
178           else:
179             fo_msg += " [no variants]"
180         if max_os_api >= constants.OS_API_V20:
181           if fo_params:
182             fo_msg += (" [parameters: %s]" %
183                        utils.CommaJoin([v[0] for v in fo_params]))
184           else:
185             fo_msg += " [no parameters]"
186         if fo_status:
187           nodes_valid[node_name] = fo_msg
188         else:
189           nodes_bad[node_name] = fo_msg
190         for hpath, hstatus, hmsg, _, _, _ in node_info:
191           nodes_hidden[node_name].append("    [hidden] path: %s, status: %s" %
192                                          (hpath, _OsStatus(hstatus, hmsg)))
193       else:
194         nodes_bad[node_name] = "OS not found"
195
196     if nodes_valid and not nodes_bad:
197       status = "valid"
198     elif not nodes_valid and nodes_bad:
199       status = "invalid"
200       has_bad = True
201     else:
202       status = "partial valid"
203       has_bad = True
204
205     def _OutputPerNodeOSStatus(msg_map):
206       map_k = utils.NiceSort(msg_map.keys())
207       for node_name in map_k:
208         ToStdout("  Node: %s, status: %s", node_name, msg_map[node_name])
209         for msg in nodes_hidden[node_name]:
210           ToStdout(msg)
211
212     ToStdout("OS: %s [global status: %s]", os_name, status)
213     if os_variants:
214       ToStdout("  Variants: [%s]" % utils.CommaJoin(os_variants))
215     _OutputPerNodeOSStatus(nodes_valid)
216     _OutputPerNodeOSStatus(nodes_bad)
217     ToStdout("")
218
219   return int(has_bad)
220
221
222 def ModifyOS(opts, args):
223   """Modify OS parameters for one OS.
224
225   @param opts: the command line options selected by the user
226   @type args: list
227   @param args: should be a list with one entry
228   @rtype: int
229   @return: the desired exit code
230
231   """
232   os = args[0]
233
234   if opts.hvparams:
235     os_hvp = {os: dict(opts.hvparams)}
236   else:
237     os_hvp = None
238
239   if opts.osparams:
240     osp = {os: opts.osparams}
241   else:
242     osp = None
243
244   if not (os_hvp or osp):
245     ToStderr("At least one of OS parameters or hypervisor parameters"
246              " must be passed")
247     return 1
248
249   op = opcodes.OpSetClusterParams(vg_name=None,
250                                   enabled_hypervisors=None,
251                                   hvparams=None,
252                                   beparams=None,
253                                   nicparams=None,
254                                   candidate_pool_size=None,
255                                   os_hvp=os_hvp,
256                                   osparams=osp)
257   SubmitOpCode(op, opts=opts)
258
259   return 0
260
261
262 commands = {
263   'list': (
264     ListOS, ARGS_NONE, [NOHDR_OPT, PRIORITY_OPT],
265     "", "Lists all valid operating systems on the cluster"),
266   'diagnose': (
267     DiagnoseOS, ARGS_NONE, [PRIORITY_OPT],
268     "", "Diagnose all operating systems"),
269   'info': (
270     ShowOSInfo, [ArgOs()], [PRIORITY_OPT],
271     "", "Show detailed information about "
272     "operating systems"),
273   'modify': (
274     ModifyOS, ARGS_ONE_OS,
275     [HVLIST_OPT, OSPARAMS_OPT, DRY_RUN_OPT, PRIORITY_OPT],
276     "", "Modify the OS parameters"),
277   }
278
279 if __name__ == '__main__':
280   sys.exit(GenericMain(commands))