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