Add support for modifying cluster OS parameters
[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 _OsStatus(status, diagnose):
74   """Beautifier function for OS status.
75
76   @type status: boolean
77   @param status: is the OS valid
78   @type diagnose: string
79   @param diagnose: the error message for invalid OSes
80   @rtype: string
81   @return: a formatted status
82
83   """
84   if status:
85     return "valid"
86   else:
87     return "invalid - %s" % diagnose
88
89 def DiagnoseOS(opts, args):
90   """Analyse all OSes on this cluster.
91
92   @param opts: the command line options selected by the user
93   @type args: list
94   @param args: should be an empty list
95   @rtype: int
96   @return: the desired exit code
97
98   """
99   op = opcodes.OpDiagnoseOS(output_fields=["name", "valid", "variants",
100                                            "node_status"], names=[])
101   result = SubmitOpCode(op, opts=opts)
102
103   if not result:
104     ToStderr("Can't get the OS list")
105     return 1
106
107   has_bad = False
108
109   for os_name, _, os_variants, node_data in result:
110     nodes_valid = {}
111     nodes_bad = {}
112     nodes_hidden = {}
113     for node_name, node_info in node_data.iteritems():
114       nodes_hidden[node_name] = []
115       if node_info: # at least one entry in the per-node list
116         (first_os_path, first_os_status, first_os_msg,
117          first_os_variants, _) = node_info.pop(0)
118         if not first_os_variants:
119           first_os_variants = []
120         first_os_msg = ("%s (path: %s) [variants: %s]" %
121                         (_OsStatus(first_os_status, first_os_msg),
122                          first_os_path, utils.CommaJoin(first_os_variants)))
123         if first_os_status:
124           nodes_valid[node_name] = first_os_msg
125         else:
126           nodes_bad[node_name] = first_os_msg
127         for hpath, hstatus, hmsg in node_info:
128           nodes_hidden[node_name].append("    [hidden] path: %s, status: %s" %
129                                          (hpath, _OsStatus(hstatus, hmsg)))
130       else:
131         nodes_bad[node_name] = "OS not found"
132
133     if nodes_valid and not nodes_bad:
134       status = "valid"
135     elif not nodes_valid and nodes_bad:
136       status = "invalid"
137       has_bad = True
138     else:
139       status = "partial valid"
140       has_bad = True
141
142     def _OutputPerNodeOSStatus(msg_map):
143       map_k = utils.NiceSort(msg_map.keys())
144       for node_name in map_k:
145         ToStdout("  Node: %s, status: %s", node_name, msg_map[node_name])
146         for msg in nodes_hidden[node_name]:
147           ToStdout(msg)
148
149     ToStdout("OS: %s [global status: %s]", os_name, status)
150     if os_variants:
151       ToStdout("  Variants: [%s]" % utils.CommaJoin(os_variants))
152     _OutputPerNodeOSStatus(nodes_valid)
153     _OutputPerNodeOSStatus(nodes_bad)
154     ToStdout("")
155
156   return int(has_bad)
157
158
159 def ModifyOS(opts, args):
160   """Modify OS parameters for one OS.
161
162   @param opts: the command line options selected by the user
163   @type args: list
164   @param args: should be a list with one entry
165   @rtype: int
166   @return: the desired exit code
167
168   """
169   os = args[0]
170
171   if opts.hvparams:
172     os_hvp = {os: dict(opts.hvparams)}
173   else:
174     os_hvp = None
175
176   if opts.osparams:
177     osp = {os: opts.osparams}
178   else:
179     osp = None
180
181   if not (os_hvp or osp):
182     ToStderr("At least one of OS parameters or hypervisor parameters"
183              " must be passed")
184     return 1
185
186   op = opcodes.OpSetClusterParams(vg_name=None,
187                                   enabled_hypervisors=None,
188                                   hvparams=None,
189                                   beparams=None,
190                                   nicparams=None,
191                                   candidate_pool_size=None,
192                                   os_hvp=os_hvp,
193                                   osparams=osp)
194   SubmitOpCode(op)
195
196   return 0
197
198
199 commands = {
200   'list': (
201     ListOS, ARGS_NONE, [NOHDR_OPT], "", "Lists all valid operating systems"
202     " on the cluster"),
203   'diagnose': (
204     DiagnoseOS, ARGS_NONE, [], "", "Diagnose all operating systems"),
205   'modify': (
206     ModifyOS, ARGS_ONE_OS, [HVLIST_OPT, OSPARAMS_OPT], "",
207     "Modify the OS parameters"),
208   }
209
210 if __name__ == '__main__':
211   sys.exit(GenericMain(commands))