gnt-debug: Test job submission as part of “test-jobqueue”
[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
140 def DiagnoseOS(opts, args):
141   """Analyse all OSes on this cluster.
142
143   @param opts: the command line options selected by the user
144   @type args: list
145   @param args: should be an empty list
146   @rtype: int
147   @return: the desired exit code
148
149   """
150   op = opcodes.OpDiagnoseOS(output_fields=["name", "valid", "variants",
151                                            "node_status"], names=[])
152   result = SubmitOpCode(op, opts=opts)
153
154   if not result:
155     ToStderr("Can't get the OS list")
156     return 1
157
158   has_bad = False
159
160   for os_name, _, os_variants, node_data in result:
161     nodes_valid = {}
162     nodes_bad = {}
163     nodes_hidden = {}
164     for node_name, node_info in node_data.iteritems():
165       nodes_hidden[node_name] = []
166       if node_info: # at least one entry in the per-node list
167         (fo_path, fo_status, fo_msg, fo_variants,
168          fo_params, fo_api) = node_info.pop(0)
169         fo_msg = "%s (path: %s)" % (_OsStatus(fo_status, fo_msg), fo_path)
170         if fo_api:
171           max_os_api = max(fo_api)
172           fo_msg += " [API versions: %s]" % utils.CommaJoin(fo_api)
173         else:
174           max_os_api = 0
175           fo_msg += " [no API versions declared]"
176         if max_os_api >= constants.OS_API_V15:
177           if fo_variants:
178             fo_msg += " [variants: %s]" % utils.CommaJoin(fo_variants)
179           else:
180             fo_msg += " [no variants]"
181         if max_os_api >= constants.OS_API_V20:
182           if fo_params:
183             fo_msg += (" [parameters: %s]" %
184                        utils.CommaJoin([v[0] for v in fo_params]))
185           else:
186             fo_msg += " [no parameters]"
187         if fo_status:
188           nodes_valid[node_name] = fo_msg
189         else:
190           nodes_bad[node_name] = fo_msg
191         for hpath, hstatus, hmsg, _, _, _ in node_info:
192           nodes_hidden[node_name].append("    [hidden] path: %s, status: %s" %
193                                          (hpath, _OsStatus(hstatus, hmsg)))
194       else:
195         nodes_bad[node_name] = "OS not found"
196
197     if nodes_valid and not nodes_bad:
198       status = "valid"
199     elif not nodes_valid and nodes_bad:
200       status = "invalid"
201       has_bad = True
202     else:
203       status = "partial valid"
204       has_bad = True
205
206     def _OutputPerNodeOSStatus(msg_map):
207       map_k = utils.NiceSort(msg_map.keys())
208       for node_name in map_k:
209         ToStdout("  Node: %s, status: %s", node_name, msg_map[node_name])
210         for msg in nodes_hidden[node_name]:
211           ToStdout(msg)
212
213     ToStdout("OS: %s [global status: %s]", os_name, status)
214     if os_variants:
215       ToStdout("  Variants: [%s]" % utils.CommaJoin(os_variants))
216     _OutputPerNodeOSStatus(nodes_valid)
217     _OutputPerNodeOSStatus(nodes_bad)
218     ToStdout("")
219
220   return int(has_bad)
221
222
223 def ModifyOS(opts, args):
224   """Modify OS parameters for one OS.
225
226   @param opts: the command line options selected by the user
227   @type args: list
228   @param args: should be a list with one entry
229   @rtype: int
230   @return: the desired exit code
231
232   """
233   os = args[0]
234
235   if opts.hvparams:
236     os_hvp = {os: dict(opts.hvparams)}
237   else:
238     os_hvp = None
239
240   if opts.osparams:
241     osp = {os: opts.osparams}
242   else:
243     osp = None
244
245   if not (os_hvp or osp):
246     ToStderr("At least one of OS parameters or hypervisor parameters"
247              " must be passed")
248     return 1
249
250   op = opcodes.OpSetClusterParams(vg_name=None,
251                                   enabled_hypervisors=None,
252                                   hvparams=None,
253                                   beparams=None,
254                                   nicparams=None,
255                                   candidate_pool_size=None,
256                                   os_hvp=os_hvp,
257                                   osparams=osp)
258   SubmitOpCode(op, opts=opts)
259
260   return 0
261
262
263 commands = {
264   'list': (
265     ListOS, ARGS_NONE, [NOHDR_OPT, PRIORITY_OPT],
266     "", "Lists all valid operating systems on the cluster"),
267   'diagnose': (
268     DiagnoseOS, ARGS_NONE, [PRIORITY_OPT],
269     "", "Diagnose all operating systems"),
270   'info': (
271     ShowOSInfo, [ArgOs()], [PRIORITY_OPT],
272     "", "Show detailed information about "
273     "operating systems"),
274   'modify': (
275     ModifyOS, ARGS_ONE_OS,
276     [HVLIST_OPT, OSPARAMS_OPT, DRY_RUN_OPT, PRIORITY_OPT],
277     "", "Modify the OS parameters"),
278   }
279
280 if __name__ == '__main__':
281   sys.exit(GenericMain(commands))