Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-node @ 2e6469a1

History | View | Annotate | Download (23 kB)

1 a8083063 Iustin Pop
#!/usr/bin/python
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 783a6c0b Iustin Pop
# Copyright (C) 2006, 2007, 2008, 2009, 2010 Google Inc.
5 a8083063 Iustin Pop
#
6 a8083063 Iustin Pop
# This program is free software; you can redistribute it and/or modify
7 a8083063 Iustin Pop
# it under the terms of the GNU General Public License as published by
8 a8083063 Iustin Pop
# the Free Software Foundation; either version 2 of the License, or
9 a8083063 Iustin Pop
# (at your option) any later version.
10 a8083063 Iustin Pop
#
11 a8083063 Iustin Pop
# This program is distributed in the hope that it will be useful, but
12 a8083063 Iustin Pop
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 a8083063 Iustin Pop
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 a8083063 Iustin Pop
# General Public License for more details.
15 a8083063 Iustin Pop
#
16 a8083063 Iustin Pop
# You should have received a copy of the GNU General Public License
17 a8083063 Iustin Pop
# along with this program; if not, write to the Free Software
18 a8083063 Iustin Pop
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 a8083063 Iustin Pop
# 02110-1301, USA.
20 a8083063 Iustin Pop
21 7260cfbe Iustin Pop
"""Node related commands"""
22 a8083063 Iustin Pop
23 2d54e29c Iustin Pop
# pylint: disable-msg=W0401,W0613,W0614,C0103
24 2f79bd34 Iustin Pop
# W0401: Wildcard import ganeti.cli
25 2d54e29c Iustin Pop
# W0613: Unused argument, since all functions follow the same API
26 2f79bd34 Iustin Pop
# W0614: Unused import %s from wildcard import (since we need cli)
27 7260cfbe Iustin Pop
# C0103: Invalid name gnt-node
28 2f79bd34 Iustin Pop
29 a8083063 Iustin Pop
import sys
30 a8083063 Iustin Pop
31 a8083063 Iustin Pop
from ganeti.cli import *
32 a8083063 Iustin Pop
from ganeti import opcodes
33 a8083063 Iustin Pop
from ganeti import utils
34 846baef9 Iustin Pop
from ganeti import constants
35 6396164f Guido Trotter
from ganeti import compat
36 c450e9b0 Iustin Pop
from ganeti import errors
37 a744b676 Manuel Franceschini
from ganeti import netutils
38 a8083063 Iustin Pop
39 a8083063 Iustin Pop
40 ebf366ee Iustin Pop
#: default list of field for L{ListNodes}
41 48c4dfa8 Iustin Pop
_LIST_DEF_FIELDS = [
42 48c4dfa8 Iustin Pop
  "name", "dtotal", "dfree",
43 48c4dfa8 Iustin Pop
  "mtotal", "mnode", "mfree",
44 48c4dfa8 Iustin Pop
  "pinst_cnt", "sinst_cnt",
45 48c4dfa8 Iustin Pop
  ]
46 48c4dfa8 Iustin Pop
47 620a85fd Iustin Pop
48 620a85fd Iustin Pop
#: default list of field for L{ListStorage}
49 620a85fd Iustin Pop
_LIST_STOR_DEF_FIELDS = [
50 620a85fd Iustin Pop
  constants.SF_NODE,
51 620a85fd Iustin Pop
  constants.SF_TYPE,
52 620a85fd Iustin Pop
  constants.SF_NAME,
53 620a85fd Iustin Pop
  constants.SF_SIZE,
54 620a85fd Iustin Pop
  constants.SF_USED,
55 620a85fd Iustin Pop
  constants.SF_FREE,
56 620a85fd Iustin Pop
  constants.SF_ALLOCATABLE,
57 620a85fd Iustin Pop
  ]
58 620a85fd Iustin Pop
59 620a85fd Iustin Pop
60 cc3bcec8 Guido Trotter
#: headers (and full field list for L{ListNodes}
61 cc3bcec8 Guido Trotter
_LIST_HEADERS = {
62 cc3bcec8 Guido Trotter
  "name": "Node", "pinst_cnt": "Pinst", "sinst_cnt": "Sinst",
63 cc3bcec8 Guido Trotter
  "pinst_list": "PriInstances", "sinst_list": "SecInstances",
64 cc3bcec8 Guido Trotter
  "pip": "PrimaryIP", "sip": "SecondaryIP",
65 cc3bcec8 Guido Trotter
  "dtotal": "DTotal", "dfree": "DFree",
66 cc3bcec8 Guido Trotter
  "mtotal": "MTotal", "mnode": "MNode", "mfree": "MFree",
67 cc3bcec8 Guido Trotter
  "bootid": "BootID",
68 0105bad3 Iustin Pop
  "ctotal": "CTotal", "cnodes": "CNodes", "csockets": "CSockets",
69 cc3bcec8 Guido Trotter
  "tags": "Tags",
70 cc3bcec8 Guido Trotter
  "serial_no": "SerialNo",
71 cc3bcec8 Guido Trotter
  "master_candidate": "MasterC",
72 cc3bcec8 Guido Trotter
  "master": "IsMaster",
73 0b2454b9 Iustin Pop
  "offline": "Offline", "drained": "Drained",
74 c120ff34 Iustin Pop
  "role": "Role",
75 033d58b0 Iustin Pop
  "ctime": "CTime", "mtime": "MTime", "uuid": "UUID"
76 cc3bcec8 Guido Trotter
  }
77 cc3bcec8 Guido Trotter
78 620a85fd Iustin Pop
79 620a85fd Iustin Pop
#: headers (and full field list for L{ListStorage}
80 620a85fd Iustin Pop
_LIST_STOR_HEADERS = {
81 620a85fd Iustin Pop
  constants.SF_NODE: "Node",
82 620a85fd Iustin Pop
  constants.SF_TYPE: "Type",
83 620a85fd Iustin Pop
  constants.SF_NAME: "Name",
84 620a85fd Iustin Pop
  constants.SF_SIZE: "Size",
85 620a85fd Iustin Pop
  constants.SF_USED: "Used",
86 620a85fd Iustin Pop
  constants.SF_FREE: "Free",
87 620a85fd Iustin Pop
  constants.SF_ALLOCATABLE: "Allocatable",
88 620a85fd Iustin Pop
  }
89 620a85fd Iustin Pop
90 620a85fd Iustin Pop
91 0e89fc2d Michael Hanselmann
#: User-facing storage unit types
92 0e89fc2d Michael Hanselmann
_USER_STORAGE_TYPE = {
93 0e89fc2d Michael Hanselmann
  constants.ST_FILE: "file",
94 0e89fc2d Michael Hanselmann
  constants.ST_LVM_PV: "lvm-pv",
95 0e89fc2d Michael Hanselmann
  constants.ST_LVM_VG: "lvm-vg",
96 0e89fc2d Michael Hanselmann
  }
97 0e89fc2d Michael Hanselmann
98 a8005e17 Michael Hanselmann
_STORAGE_TYPE_OPT = \
99 aeaefce8 Iustin Pop
  cli_option("-t", "--storage-type",
100 a8005e17 Michael Hanselmann
             dest="user_storage_type",
101 a8005e17 Michael Hanselmann
             choices=_USER_STORAGE_TYPE.keys(),
102 a8005e17 Michael Hanselmann
             default=None,
103 a8005e17 Michael Hanselmann
             metavar="STORAGE_TYPE",
104 ab3e6da8 Iustin Pop
             help=("Storage type (%s)" %
105 ab3e6da8 Iustin Pop
                   utils.CommaJoin(_USER_STORAGE_TYPE.keys())))
106 a8005e17 Michael Hanselmann
107 a8005e17 Michael Hanselmann
_REPAIRABLE_STORAGE_TYPES = \
108 a8005e17 Michael Hanselmann
  [st for st, so in constants.VALID_STORAGE_OPERATIONS.iteritems()
109 a8005e17 Michael Hanselmann
   if constants.SO_FIX_CONSISTENCY in so]
110 a8005e17 Michael Hanselmann
111 a8005e17 Michael Hanselmann
_MODIFIABLE_STORAGE_TYPES = constants.MODIFIABLE_STORAGE_FIELDS.keys()
112 a8005e17 Michael Hanselmann
113 51144e33 Michael Hanselmann
114 2e6469a1 René Nussbaumer
NONODE_SETUP_OPT = cli_option("--no-node-setup", default=True,
115 2e6469a1 René Nussbaumer
                              action="store_false", dest="node_setup",
116 2e6469a1 René Nussbaumer
                              help=("Do not make initial SSH setup on remote"
117 2e6469a1 René Nussbaumer
                                    " node (needs to be done manually)"))
118 2e6469a1 René Nussbaumer
119 2e6469a1 René Nussbaumer
120 86f5eae3 Michael Hanselmann
def ConvertStorageType(user_storage_type):
121 86f5eae3 Michael Hanselmann
  """Converts a user storage type to its internal name.
122 86f5eae3 Michael Hanselmann
123 86f5eae3 Michael Hanselmann
  """
124 86f5eae3 Michael Hanselmann
  try:
125 86f5eae3 Michael Hanselmann
    return _USER_STORAGE_TYPE[user_storage_type]
126 86f5eae3 Michael Hanselmann
  except KeyError:
127 debac808 Iustin Pop
    raise errors.OpPrereqError("Unknown storage type: %s" % user_storage_type,
128 debac808 Iustin Pop
                               errors.ECODE_INVAL)
129 86f5eae3 Michael Hanselmann
130 86f5eae3 Michael Hanselmann
131 2e6469a1 René Nussbaumer
def _RunSetupSSH(options, nodes):
132 2e6469a1 René Nussbaumer
  """Wrapper around utils.RunCmd to call setup-ssh
133 2e6469a1 René Nussbaumer
134 2e6469a1 René Nussbaumer
  @param options: The command line options
135 2e6469a1 René Nussbaumer
  @param nodes: The nodes to setup
136 2e6469a1 René Nussbaumer
137 2e6469a1 René Nussbaumer
  """
138 2e6469a1 René Nussbaumer
  cmd = [constants.SETUP_SSH]
139 2e6469a1 René Nussbaumer
140 2e6469a1 René Nussbaumer
  # Pass --debug|--verbose to the external script if set on our invocation
141 2e6469a1 René Nussbaumer
  # --debug overrides --verbose
142 2e6469a1 René Nussbaumer
  if options.debug:
143 2e6469a1 René Nussbaumer
    cmd.append("--debug")
144 2e6469a1 René Nussbaumer
  elif options.verbose:
145 2e6469a1 René Nussbaumer
    cmd.append("--verbose")
146 2e6469a1 René Nussbaumer
147 2e6469a1 René Nussbaumer
  cmd.extend(nodes)
148 2e6469a1 René Nussbaumer
149 2e6469a1 René Nussbaumer
  result = utils.RunCmd(cmd, interactive=True)
150 2e6469a1 René Nussbaumer
151 2e6469a1 René Nussbaumer
  if result.failed:
152 2e6469a1 René Nussbaumer
    errmsg = ("Command '%s' failed with exit code %s; output %r" %
153 2e6469a1 René Nussbaumer
              (result.cmd, result.exit_code, result.output))
154 2e6469a1 René Nussbaumer
    raise errors.OpExecError(errmsg)
155 2e6469a1 René Nussbaumer
156 2e6469a1 René Nussbaumer
157 4331f6cd Michael Hanselmann
@UsesRPC
158 a8083063 Iustin Pop
def AddNode(opts, args):
159 ebf366ee Iustin Pop
  """Add a node to the cluster.
160 ebf366ee Iustin Pop
161 ebf366ee Iustin Pop
  @param opts: the command line options selected by the user
162 ebf366ee Iustin Pop
  @type args: list
163 ebf366ee Iustin Pop
  @param args: should contain only one element, the new node name
164 ebf366ee Iustin Pop
  @rtype: int
165 ebf366ee Iustin Pop
  @return: the desired exit code
166 05ccd983 Guido Trotter
167 05ccd983 Guido Trotter
  """
168 87622829 Iustin Pop
  cl = GetClient()
169 b705c7a6 Manuel Franceschini
  node = netutils.GetHostname(name=args[0]).name
170 82e12743 Iustin Pop
  readd = opts.readd
171 82e12743 Iustin Pop
172 82e12743 Iustin Pop
  try:
173 82e12743 Iustin Pop
    output = cl.QueryNodes(names=[node], fields=['name', 'sip'],
174 77921a95 Iustin Pop
                           use_locking=False)
175 82e12743 Iustin Pop
    node_exists, sip = output[0]
176 82e12743 Iustin Pop
  except (errors.OpPrereqError, errors.OpExecError):
177 82e12743 Iustin Pop
    node_exists = ""
178 82e12743 Iustin Pop
    sip = None
179 82e12743 Iustin Pop
180 82e12743 Iustin Pop
  if readd:
181 82e12743 Iustin Pop
    if not node_exists:
182 82e12743 Iustin Pop
      ToStderr("Node %s not in the cluster"
183 82e12743 Iustin Pop
               " - please retry without '--readd'", node)
184 82e12743 Iustin Pop
      return 1
185 82e12743 Iustin Pop
  else:
186 82e12743 Iustin Pop
    if node_exists:
187 3a24c527 Iustin Pop
      ToStderr("Node %s already in the cluster (as %s)"
188 82e12743 Iustin Pop
               " - please retry with '--readd'", node, node_exists)
189 05ccd983 Guido Trotter
      return 1
190 82e12743 Iustin Pop
    sip = opts.secondary_ip
191 05ccd983 Guido Trotter
192 87622829 Iustin Pop
  # read the cluster name from the master
193 87622829 Iustin Pop
  output = cl.QueryConfigValues(['cluster_name'])
194 87622829 Iustin Pop
195 a8ae3eb5 Iustin Pop
  if not readd:
196 82e12743 Iustin Pop
    ToStderr("-- WARNING -- \n"
197 82e12743 Iustin Pop
             "Performing this operation is going to replace the ssh daemon"
198 82e12743 Iustin Pop
             " keypair\n"
199 82e12743 Iustin Pop
             "on the target machine (%s) with the ones of the"
200 82e12743 Iustin Pop
             " current one\n"
201 82e12743 Iustin Pop
             "and grant full intra-cluster ssh root access to/from it\n", node)
202 05ccd983 Guido Trotter
203 2e6469a1 René Nussbaumer
  if opts.node_setup:
204 2e6469a1 René Nussbaumer
    _RunSetupSSH(opts, [node])
205 827f753e Guido Trotter
206 82e12743 Iustin Pop
  op = opcodes.OpAddNode(node_name=args[0], secondary_ip=sip,
207 e7c6e02b Michael Hanselmann
                         readd=opts.readd)
208 400ca2f7 Iustin Pop
  SubmitOpCode(op, opts=opts)
209 a8083063 Iustin Pop
210 a8083063 Iustin Pop
211 a8083063 Iustin Pop
def ListNodes(opts, args):
212 a8083063 Iustin Pop
  """List nodes and their properties.
213 a8083063 Iustin Pop
214 ebf366ee Iustin Pop
  @param opts: the command line options selected by the user
215 ebf366ee Iustin Pop
  @type args: list
216 ebf366ee Iustin Pop
  @param args: should be an empty list
217 ebf366ee Iustin Pop
  @rtype: int
218 ebf366ee Iustin Pop
  @return: the desired exit code
219 ebf366ee Iustin Pop
220 a8083063 Iustin Pop
  """
221 a8083063 Iustin Pop
  if opts.output is None:
222 48c4dfa8 Iustin Pop
    selected_fields = _LIST_DEF_FIELDS
223 48c4dfa8 Iustin Pop
  elif opts.output.startswith("+"):
224 48c4dfa8 Iustin Pop
    selected_fields = _LIST_DEF_FIELDS + opts.output[1:].split(",")
225 a8083063 Iustin Pop
  else:
226 a8083063 Iustin Pop
    selected_fields = opts.output.split(",")
227 a8083063 Iustin Pop
228 f1de3563 Iustin Pop
  output = GetClient().QueryNodes(args, selected_fields, opts.do_locking)
229 a8083063 Iustin Pop
230 a8083063 Iustin Pop
  if not opts.no_headers:
231 cc3bcec8 Guido Trotter
    headers = _LIST_HEADERS
232 137161c9 Michael Hanselmann
  else:
233 137161c9 Michael Hanselmann
    headers = None
234 137161c9 Michael Hanselmann
235 9fbfbb7b Iustin Pop
  unitfields = ["dtotal", "dfree", "mtotal", "mnode", "mfree"]
236 137161c9 Michael Hanselmann
237 ec223efb Iustin Pop
  numfields = ["dtotal", "dfree",
238 ec223efb Iustin Pop
               "mtotal", "mnode", "mfree",
239 e8a4c138 Iustin Pop
               "pinst_cnt", "sinst_cnt",
240 38d7239a Iustin Pop
               "ctotal", "serial_no"]
241 ec223efb Iustin Pop
242 130a6a6f Iustin Pop
  list_type_fields = ("pinst_list", "sinst_list", "tags")
243 ec223efb Iustin Pop
  # change raw values to nicer strings
244 ec223efb Iustin Pop
  for row in output:
245 ec223efb Iustin Pop
    for idx, field in enumerate(selected_fields):
246 ec223efb Iustin Pop
      val = row[idx]
247 130a6a6f Iustin Pop
      if field in list_type_fields:
248 ec223efb Iustin Pop
        val = ",".join(val)
249 0b2454b9 Iustin Pop
      elif field in ('master', 'master_candidate', 'offline', 'drained'):
250 0e67cdbe Iustin Pop
        if val:
251 0e67cdbe Iustin Pop
          val = 'Y'
252 0e67cdbe Iustin Pop
        else:
253 0e67cdbe Iustin Pop
          val = 'N'
254 90f72445 Iustin Pop
      elif field == "ctime" or field == "mtime":
255 90f72445 Iustin Pop
        val = utils.FormatTime(val)
256 ec223efb Iustin Pop
      elif val is None:
257 ec223efb Iustin Pop
        val = "?"
258 cc5b94db Guido Trotter
      elif opts.roman_integers and isinstance(val, int):
259 6396164f Guido Trotter
        val = compat.TryToRoman(val)
260 ec223efb Iustin Pop
      row[idx] = str(val)
261 137161c9 Michael Hanselmann
262 16be8703 Iustin Pop
  data = GenerateTable(separator=opts.separator, headers=headers,
263 16be8703 Iustin Pop
                       fields=selected_fields, unitfields=unitfields,
264 9fbfbb7b Iustin Pop
                       numfields=numfields, data=output, units=opts.units)
265 16be8703 Iustin Pop
  for line in data:
266 3a24c527 Iustin Pop
    ToStdout(line)
267 a8083063 Iustin Pop
268 a8083063 Iustin Pop
  return 0
269 a8083063 Iustin Pop
270 a8083063 Iustin Pop
271 a5bc662a Iustin Pop
def EvacuateNode(opts, args):
272 a5bc662a Iustin Pop
  """Relocate all secondary instance from a node.
273 a5bc662a Iustin Pop
274 ebf366ee Iustin Pop
  @param opts: the command line options selected by the user
275 ebf366ee Iustin Pop
  @type args: list
276 ebf366ee Iustin Pop
  @param args: should be an empty list
277 ebf366ee Iustin Pop
  @rtype: int
278 ebf366ee Iustin Pop
  @return: the desired exit code
279 ebf366ee Iustin Pop
280 a5bc662a Iustin Pop
  """
281 479636a3 Iustin Pop
  cl = GetClient()
282 a5bc662a Iustin Pop
  force = opts.force
283 c4ed32cb Iustin Pop
284 c4ed32cb Iustin Pop
  dst_node = opts.dst_node
285 c4ed32cb Iustin Pop
  iallocator = opts.iallocator
286 c4ed32cb Iustin Pop
287 f8c9fa5c Iustin Pop
  op = opcodes.OpNodeEvacuationStrategy(nodes=args,
288 f8c9fa5c Iustin Pop
                                        iallocator=iallocator,
289 f8c9fa5c Iustin Pop
                                        remote_node=dst_node)
290 f8c9fa5c Iustin Pop
291 f8c9fa5c Iustin Pop
  result = SubmitOpCode(op, cl=cl, opts=opts)
292 f8c9fa5c Iustin Pop
  if not result:
293 f8c9fa5c Iustin Pop
    # no instances to migrate
294 f8c9fa5c Iustin Pop
    ToStderr("No secondary instances on node(s) %s, exiting.",
295 f8c9fa5c Iustin Pop
             utils.CommaJoin(args))
296 a5bc662a Iustin Pop
    return constants.EXIT_SUCCESS
297 a5bc662a Iustin Pop
298 f8c9fa5c Iustin Pop
  if not force and not AskUser("Relocate instance(s) %s from node(s) %s?" %
299 f8c9fa5c Iustin Pop
                               (",".join("'%s'" % name[0] for name in result),
300 f8c9fa5c Iustin Pop
                               utils.CommaJoin(args))):
301 a5bc662a Iustin Pop
    return constants.EXIT_CONFIRMATION
302 a5bc662a Iustin Pop
303 f8c9fa5c Iustin Pop
  jex = JobExecutor(cl=cl, opts=opts)
304 f8c9fa5c Iustin Pop
  for row in result:
305 f8c9fa5c Iustin Pop
    iname = row[0]
306 f8c9fa5c Iustin Pop
    node = row[1]
307 f8c9fa5c Iustin Pop
    ToStdout("Will relocate instance %s to node %s", iname, node)
308 f8c9fa5c Iustin Pop
    op = opcodes.OpReplaceDisks(instance_name=iname,
309 f8c9fa5c Iustin Pop
                                remote_node=node, disks=[],
310 f8c9fa5c Iustin Pop
                                mode=constants.REPLACE_DISK_CHG,
311 f8c9fa5c Iustin Pop
                                early_release=opts.early_release)
312 f8c9fa5c Iustin Pop
    jex.QueueJob(iname, op)
313 f8c9fa5c Iustin Pop
  results = jex.GetResults()
314 f8c9fa5c Iustin Pop
  bad_cnt = len([row for row in results if not row[0]])
315 f8c9fa5c Iustin Pop
  if bad_cnt == 0:
316 f8c9fa5c Iustin Pop
    ToStdout("All %d instance(s) failed over successfully.", len(results))
317 f8c9fa5c Iustin Pop
    rcode = constants.EXIT_SUCCESS
318 f8c9fa5c Iustin Pop
  else:
319 f8c9fa5c Iustin Pop
    ToStdout("There were errors during the failover:\n"
320 f8c9fa5c Iustin Pop
             "%d error(s) out of %d instance(s).", bad_cnt, len(results))
321 f8c9fa5c Iustin Pop
    rcode = constants.EXIT_FAILURE
322 f8c9fa5c Iustin Pop
  return rcode
323 a5bc662a Iustin Pop
324 a5bc662a Iustin Pop
325 c450e9b0 Iustin Pop
def FailoverNode(opts, args):
326 c450e9b0 Iustin Pop
  """Failover all primary instance on a node.
327 c450e9b0 Iustin Pop
328 ebf366ee Iustin Pop
  @param opts: the command line options selected by the user
329 ebf366ee Iustin Pop
  @type args: list
330 ebf366ee Iustin Pop
  @param args: should be an empty list
331 ebf366ee Iustin Pop
  @rtype: int
332 ebf366ee Iustin Pop
  @return: the desired exit code
333 ebf366ee Iustin Pop
334 c450e9b0 Iustin Pop
  """
335 479636a3 Iustin Pop
  cl = GetClient()
336 c450e9b0 Iustin Pop
  force = opts.force
337 c450e9b0 Iustin Pop
  selected_fields = ["name", "pinst_list"]
338 c450e9b0 Iustin Pop
339 2e7b8369 Iustin Pop
  # these fields are static data anyway, so it doesn't matter, but
340 2e7b8369 Iustin Pop
  # locking=True should be safer
341 2e7b8369 Iustin Pop
  result = cl.QueryNodes(names=args, fields=selected_fields,
342 77921a95 Iustin Pop
                         use_locking=False)
343 c450e9b0 Iustin Pop
  node, pinst = result[0]
344 c450e9b0 Iustin Pop
345 c450e9b0 Iustin Pop
  if not pinst:
346 3a24c527 Iustin Pop
    ToStderr("No primary instances on node %s, exiting.", node)
347 c450e9b0 Iustin Pop
    return 0
348 c450e9b0 Iustin Pop
349 c450e9b0 Iustin Pop
  pinst = utils.NiceSort(pinst)
350 c450e9b0 Iustin Pop
351 c450e9b0 Iustin Pop
  retcode = 0
352 c450e9b0 Iustin Pop
353 c450e9b0 Iustin Pop
  if not force and not AskUser("Fail over instance(s) %s?" %
354 c450e9b0 Iustin Pop
                               (",".join("'%s'" % name for name in pinst))):
355 c450e9b0 Iustin Pop
    return 2
356 c450e9b0 Iustin Pop
357 cb573a31 Iustin Pop
  jex = JobExecutor(cl=cl, opts=opts)
358 c450e9b0 Iustin Pop
  for iname in pinst:
359 c450e9b0 Iustin Pop
    op = opcodes.OpFailoverInstance(instance_name=iname,
360 c450e9b0 Iustin Pop
                                    ignore_consistency=opts.ignore_consistency)
361 479636a3 Iustin Pop
    jex.QueueJob(iname, op)
362 479636a3 Iustin Pop
  results = jex.GetResults()
363 479636a3 Iustin Pop
  bad_cnt = len([row for row in results if not row[0]])
364 479636a3 Iustin Pop
  if bad_cnt == 0:
365 479636a3 Iustin Pop
    ToStdout("All %d instance(s) failed over successfully.", len(results))
366 c450e9b0 Iustin Pop
  else:
367 3a24c527 Iustin Pop
    ToStdout("There were errors during the failover:\n"
368 479636a3 Iustin Pop
             "%d error(s) out of %d instance(s).", bad_cnt, len(results))
369 c450e9b0 Iustin Pop
  return retcode
370 c450e9b0 Iustin Pop
371 c450e9b0 Iustin Pop
372 40ef0ed6 Iustin Pop
def MigrateNode(opts, args):
373 40ef0ed6 Iustin Pop
  """Migrate all primary instance on a node.
374 40ef0ed6 Iustin Pop
375 40ef0ed6 Iustin Pop
  """
376 40ef0ed6 Iustin Pop
  cl = GetClient()
377 40ef0ed6 Iustin Pop
  force = opts.force
378 40ef0ed6 Iustin Pop
  selected_fields = ["name", "pinst_list"]
379 40ef0ed6 Iustin Pop
380 77921a95 Iustin Pop
  result = cl.QueryNodes(names=args, fields=selected_fields, use_locking=False)
381 40ef0ed6 Iustin Pop
  node, pinst = result[0]
382 40ef0ed6 Iustin Pop
383 40ef0ed6 Iustin Pop
  if not pinst:
384 40ef0ed6 Iustin Pop
    ToStdout("No primary instances on node %s, exiting." % node)
385 40ef0ed6 Iustin Pop
    return 0
386 40ef0ed6 Iustin Pop
387 40ef0ed6 Iustin Pop
  pinst = utils.NiceSort(pinst)
388 40ef0ed6 Iustin Pop
389 40ef0ed6 Iustin Pop
  if not force and not AskUser("Migrate instance(s) %s?" %
390 40ef0ed6 Iustin Pop
                               (",".join("'%s'" % name for name in pinst))):
391 40ef0ed6 Iustin Pop
    return 2
392 40ef0ed6 Iustin Pop
393 e71b9ef4 Iustin Pop
  # this should be removed once --non-live is deprecated
394 783a6c0b Iustin Pop
  if not opts.live and opts.migration_mode is not None:
395 e71b9ef4 Iustin Pop
    raise errors.OpPrereqError("Only one of the --non-live and "
396 783a6c0b Iustin Pop
                               "--migration-mode options can be passed",
397 e71b9ef4 Iustin Pop
                               errors.ECODE_INVAL)
398 e71b9ef4 Iustin Pop
  if not opts.live: # --non-live passed
399 8c35561f Iustin Pop
    mode = constants.HT_MIGRATION_NONLIVE
400 e71b9ef4 Iustin Pop
  else:
401 8c35561f Iustin Pop
    mode = opts.migration_mode
402 8c35561f Iustin Pop
  op = opcodes.OpMigrateNode(node_name=args[0], mode=mode)
403 400ca2f7 Iustin Pop
  SubmitOpCode(op, cl=cl, opts=opts)
404 40ef0ed6 Iustin Pop
405 40ef0ed6 Iustin Pop
406 a8083063 Iustin Pop
def ShowNodeConfig(opts, args):
407 a8083063 Iustin Pop
  """Show node information.
408 a8083063 Iustin Pop
409 ebf366ee Iustin Pop
  @param opts: the command line options selected by the user
410 ebf366ee Iustin Pop
  @type args: list
411 ebf366ee Iustin Pop
  @param args: should either be an empty list, in which case
412 ebf366ee Iustin Pop
      we show information about all nodes, or should contain
413 ebf366ee Iustin Pop
      a list of nodes to be queried for information
414 ebf366ee Iustin Pop
  @rtype: int
415 ebf366ee Iustin Pop
  @return: the desired exit code
416 ebf366ee Iustin Pop
417 a8083063 Iustin Pop
  """
418 2e7b8369 Iustin Pop
  cl = GetClient()
419 2e7b8369 Iustin Pop
  result = cl.QueryNodes(fields=["name", "pip", "sip",
420 0b2454b9 Iustin Pop
                                 "pinst_list", "sinst_list",
421 0b2454b9 Iustin Pop
                                 "master_candidate", "drained", "offline"],
422 77921a95 Iustin Pop
                         names=args, use_locking=False)
423 a8083063 Iustin Pop
424 0b2454b9 Iustin Pop
  for (name, primary_ip, secondary_ip, pinst, sinst,
425 0b2454b9 Iustin Pop
       is_mc, drained, offline) in result:
426 3a24c527 Iustin Pop
    ToStdout("Node name: %s", name)
427 3a24c527 Iustin Pop
    ToStdout("  primary ip: %s", primary_ip)
428 3a24c527 Iustin Pop
    ToStdout("  secondary ip: %s", secondary_ip)
429 0b2454b9 Iustin Pop
    ToStdout("  master candidate: %s", is_mc)
430 0b2454b9 Iustin Pop
    ToStdout("  drained: %s", drained)
431 0b2454b9 Iustin Pop
    ToStdout("  offline: %s", offline)
432 a8083063 Iustin Pop
    if pinst:
433 3a24c527 Iustin Pop
      ToStdout("  primary for instances:")
434 ae07a1d3 Iustin Pop
      for iname in utils.NiceSort(pinst):
435 3a24c527 Iustin Pop
        ToStdout("    - %s", iname)
436 a8083063 Iustin Pop
    else:
437 3a24c527 Iustin Pop
      ToStdout("  primary for no instances")
438 a8083063 Iustin Pop
    if sinst:
439 3a24c527 Iustin Pop
      ToStdout("  secondary for instances:")
440 ae07a1d3 Iustin Pop
      for iname in utils.NiceSort(sinst):
441 3a24c527 Iustin Pop
        ToStdout("    - %s", iname)
442 a8083063 Iustin Pop
    else:
443 3a24c527 Iustin Pop
      ToStdout("  secondary for no instances")
444 a8083063 Iustin Pop
445 a8083063 Iustin Pop
  return 0
446 a8083063 Iustin Pop
447 a8083063 Iustin Pop
448 a8083063 Iustin Pop
def RemoveNode(opts, args):
449 ebf366ee Iustin Pop
  """Remove a node from the cluster.
450 ebf366ee Iustin Pop
451 ebf366ee Iustin Pop
  @param opts: the command line options selected by the user
452 ebf366ee Iustin Pop
  @type args: list
453 ebf366ee Iustin Pop
  @param args: should contain only one element, the name of
454 ebf366ee Iustin Pop
      the node to be removed
455 ebf366ee Iustin Pop
  @rtype: int
456 ebf366ee Iustin Pop
  @return: the desired exit code
457 ebf366ee Iustin Pop
458 ebf366ee Iustin Pop
  """
459 a8083063 Iustin Pop
  op = opcodes.OpRemoveNode(node_name=args[0])
460 400ca2f7 Iustin Pop
  SubmitOpCode(op, opts=opts)
461 ebf366ee Iustin Pop
  return 0
462 a8083063 Iustin Pop
463 a8083063 Iustin Pop
464 f5118ade Iustin Pop
def PowercycleNode(opts, args):
465 f5118ade Iustin Pop
  """Remove a node from the cluster.
466 f5118ade Iustin Pop
467 f5118ade Iustin Pop
  @param opts: the command line options selected by the user
468 f5118ade Iustin Pop
  @type args: list
469 f5118ade Iustin Pop
  @param args: should contain only one element, the name of
470 f5118ade Iustin Pop
      the node to be removed
471 f5118ade Iustin Pop
  @rtype: int
472 f5118ade Iustin Pop
  @return: the desired exit code
473 f5118ade Iustin Pop
474 f5118ade Iustin Pop
  """
475 f5118ade Iustin Pop
  node = args[0]
476 f5118ade Iustin Pop
  if (not opts.confirm and
477 f5118ade Iustin Pop
      not AskUser("Are you sure you want to hard powercycle node %s?" % node)):
478 f5118ade Iustin Pop
    return 2
479 f5118ade Iustin Pop
480 f5118ade Iustin Pop
  op = opcodes.OpPowercycleNode(node_name=node, force=opts.force)
481 400ca2f7 Iustin Pop
  result = SubmitOpCode(op, opts=opts)
482 f5118ade Iustin Pop
  ToStderr(result)
483 f5118ade Iustin Pop
  return 0
484 f5118ade Iustin Pop
485 f5118ade Iustin Pop
486 dcb93971 Michael Hanselmann
def ListVolumes(opts, args):
487 dcb93971 Michael Hanselmann
  """List logical volumes on node(s).
488 dcb93971 Michael Hanselmann
489 ebf366ee Iustin Pop
  @param opts: the command line options selected by the user
490 ebf366ee Iustin Pop
  @type args: list
491 ebf366ee Iustin Pop
  @param args: should either be an empty list, in which case
492 ebf366ee Iustin Pop
      we list data for all nodes, or contain a list of nodes
493 ebf366ee Iustin Pop
      to display data only for those
494 ebf366ee Iustin Pop
  @rtype: int
495 ebf366ee Iustin Pop
  @return: the desired exit code
496 ebf366ee Iustin Pop
497 dcb93971 Michael Hanselmann
  """
498 dcb93971 Michael Hanselmann
  if opts.output is None:
499 dcb93971 Michael Hanselmann
    selected_fields = ["node", "phys", "vg",
500 dcb93971 Michael Hanselmann
                       "name", "size", "instance"]
501 dcb93971 Michael Hanselmann
  else:
502 dcb93971 Michael Hanselmann
    selected_fields = opts.output.split(",")
503 dcb93971 Michael Hanselmann
504 dcb93971 Michael Hanselmann
  op = opcodes.OpQueryNodeVolumes(nodes=args, output_fields=selected_fields)
505 400ca2f7 Iustin Pop
  output = SubmitOpCode(op, opts=opts)
506 dcb93971 Michael Hanselmann
507 dcb93971 Michael Hanselmann
  if not opts.no_headers:
508 137161c9 Michael Hanselmann
    headers = {"node": "Node", "phys": "PhysDev",
509 137161c9 Michael Hanselmann
               "vg": "VG", "name": "Name",
510 137161c9 Michael Hanselmann
               "size": "Size", "instance": "Instance"}
511 137161c9 Michael Hanselmann
  else:
512 137161c9 Michael Hanselmann
    headers = None
513 137161c9 Michael Hanselmann
514 9fbfbb7b Iustin Pop
  unitfields = ["size"]
515 137161c9 Michael Hanselmann
516 137161c9 Michael Hanselmann
  numfields = ["size"]
517 137161c9 Michael Hanselmann
518 16be8703 Iustin Pop
  data = GenerateTable(separator=opts.separator, headers=headers,
519 16be8703 Iustin Pop
                       fields=selected_fields, unitfields=unitfields,
520 9fbfbb7b Iustin Pop
                       numfields=numfields, data=output, units=opts.units)
521 16be8703 Iustin Pop
522 16be8703 Iustin Pop
  for line in data:
523 3a24c527 Iustin Pop
    ToStdout(line)
524 dcb93971 Michael Hanselmann
525 dcb93971 Michael Hanselmann
  return 0
526 dcb93971 Michael Hanselmann
527 dcb93971 Michael Hanselmann
528 9b94905f Iustin Pop
def ListStorage(opts, args):
529 4007f57d Michael Hanselmann
  """List physical volumes on node(s).
530 4007f57d Michael Hanselmann
531 4007f57d Michael Hanselmann
  @param opts: the command line options selected by the user
532 4007f57d Michael Hanselmann
  @type args: list
533 4007f57d Michael Hanselmann
  @param args: should either be an empty list, in which case
534 4007f57d Michael Hanselmann
      we list data for all nodes, or contain a list of nodes
535 4007f57d Michael Hanselmann
      to display data only for those
536 4007f57d Michael Hanselmann
  @rtype: int
537 4007f57d Michael Hanselmann
  @return: the desired exit code
538 4007f57d Michael Hanselmann
539 4007f57d Michael Hanselmann
  """
540 53548798 Michael Hanselmann
  # TODO: Default to ST_FILE if LVM is disabled on the cluster
541 53548798 Michael Hanselmann
  if opts.user_storage_type is None:
542 53548798 Michael Hanselmann
    opts.user_storage_type = constants.ST_LVM_PV
543 53548798 Michael Hanselmann
544 86f5eae3 Michael Hanselmann
  storage_type = ConvertStorageType(opts.user_storage_type)
545 53548798 Michael Hanselmann
546 4007f57d Michael Hanselmann
  if opts.output is None:
547 620a85fd Iustin Pop
    selected_fields = _LIST_STOR_DEF_FIELDS
548 dc09c3cf Iustin Pop
  elif opts.output.startswith("+"):
549 620a85fd Iustin Pop
    selected_fields = _LIST_STOR_DEF_FIELDS + opts.output[1:].split(",")
550 4007f57d Michael Hanselmann
  else:
551 4007f57d Michael Hanselmann
    selected_fields = opts.output.split(",")
552 4007f57d Michael Hanselmann
553 4007f57d Michael Hanselmann
  op = opcodes.OpQueryNodeStorage(nodes=args,
554 53548798 Michael Hanselmann
                                  storage_type=storage_type,
555 4007f57d Michael Hanselmann
                                  output_fields=selected_fields)
556 400ca2f7 Iustin Pop
  output = SubmitOpCode(op, opts=opts)
557 4007f57d Michael Hanselmann
558 4007f57d Michael Hanselmann
  if not opts.no_headers:
559 4007f57d Michael Hanselmann
    headers = {
560 620a85fd Iustin Pop
      constants.SF_NODE: "Node",
561 620a85fd Iustin Pop
      constants.SF_TYPE: "Type",
562 4007f57d Michael Hanselmann
      constants.SF_NAME: "Name",
563 4007f57d Michael Hanselmann
      constants.SF_SIZE: "Size",
564 4007f57d Michael Hanselmann
      constants.SF_USED: "Used",
565 4007f57d Michael Hanselmann
      constants.SF_FREE: "Free",
566 4007f57d Michael Hanselmann
      constants.SF_ALLOCATABLE: "Allocatable",
567 4007f57d Michael Hanselmann
      }
568 4007f57d Michael Hanselmann
  else:
569 4007f57d Michael Hanselmann
    headers = None
570 4007f57d Michael Hanselmann
571 4007f57d Michael Hanselmann
  unitfields = [constants.SF_SIZE, constants.SF_USED, constants.SF_FREE]
572 4007f57d Michael Hanselmann
  numfields = [constants.SF_SIZE, constants.SF_USED, constants.SF_FREE]
573 4007f57d Michael Hanselmann
574 dc09c3cf Iustin Pop
  # change raw values to nicer strings
575 dc09c3cf Iustin Pop
  for row in output:
576 dc09c3cf Iustin Pop
    for idx, field in enumerate(selected_fields):
577 dc09c3cf Iustin Pop
      val = row[idx]
578 dc09c3cf Iustin Pop
      if field == constants.SF_ALLOCATABLE:
579 dc09c3cf Iustin Pop
        if val:
580 dc09c3cf Iustin Pop
          val = "Y"
581 dc09c3cf Iustin Pop
        else:
582 dc09c3cf Iustin Pop
          val = "N"
583 dc09c3cf Iustin Pop
      row[idx] = str(val)
584 dc09c3cf Iustin Pop
585 4007f57d Michael Hanselmann
  data = GenerateTable(separator=opts.separator, headers=headers,
586 4007f57d Michael Hanselmann
                       fields=selected_fields, unitfields=unitfields,
587 4007f57d Michael Hanselmann
                       numfields=numfields, data=output, units=opts.units)
588 4007f57d Michael Hanselmann
589 4007f57d Michael Hanselmann
  for line in data:
590 4007f57d Michael Hanselmann
    ToStdout(line)
591 4007f57d Michael Hanselmann
592 4007f57d Michael Hanselmann
  return 0
593 4007f57d Michael Hanselmann
594 4007f57d Michael Hanselmann
595 9b94905f Iustin Pop
def ModifyStorage(opts, args):
596 0e89fc2d Michael Hanselmann
  """Modify storage volume on a node.
597 0e89fc2d Michael Hanselmann
598 0e89fc2d Michael Hanselmann
  @param opts: the command line options selected by the user
599 0e89fc2d Michael Hanselmann
  @type args: list
600 0e89fc2d Michael Hanselmann
  @param args: should contain 3 items: node name, storage type and volume name
601 0e89fc2d Michael Hanselmann
  @rtype: int
602 0e89fc2d Michael Hanselmann
  @return: the desired exit code
603 0e89fc2d Michael Hanselmann
604 0e89fc2d Michael Hanselmann
  """
605 0e89fc2d Michael Hanselmann
  (node_name, user_storage_type, volume_name) = args
606 0e89fc2d Michael Hanselmann
607 86f5eae3 Michael Hanselmann
  storage_type = ConvertStorageType(user_storage_type)
608 0e89fc2d Michael Hanselmann
609 0e89fc2d Michael Hanselmann
  changes = {}
610 0e89fc2d Michael Hanselmann
611 0e89fc2d Michael Hanselmann
  if opts.allocatable is not None:
612 e7b61bb0 Iustin Pop
    changes[constants.SF_ALLOCATABLE] = opts.allocatable
613 0e89fc2d Michael Hanselmann
614 0e89fc2d Michael Hanselmann
  if changes:
615 0e89fc2d Michael Hanselmann
    op = opcodes.OpModifyNodeStorage(node_name=node_name,
616 0e89fc2d Michael Hanselmann
                                     storage_type=storage_type,
617 0e89fc2d Michael Hanselmann
                                     name=volume_name,
618 0e89fc2d Michael Hanselmann
                                     changes=changes)
619 400ca2f7 Iustin Pop
    SubmitOpCode(op, opts=opts)
620 620a85fd Iustin Pop
  else:
621 620a85fd Iustin Pop
    ToStderr("No changes to perform, exiting.")
622 0e89fc2d Michael Hanselmann
623 0e89fc2d Michael Hanselmann
624 9b94905f Iustin Pop
def RepairStorage(opts, args):
625 1e3463f1 Michael Hanselmann
  """Repairs a storage volume on a node.
626 1e3463f1 Michael Hanselmann
627 1e3463f1 Michael Hanselmann
  @param opts: the command line options selected by the user
628 1e3463f1 Michael Hanselmann
  @type args: list
629 1e3463f1 Michael Hanselmann
  @param args: should contain 3 items: node name, storage type and volume name
630 1e3463f1 Michael Hanselmann
  @rtype: int
631 1e3463f1 Michael Hanselmann
  @return: the desired exit code
632 1e3463f1 Michael Hanselmann
633 1e3463f1 Michael Hanselmann
  """
634 1e3463f1 Michael Hanselmann
  (node_name, user_storage_type, volume_name) = args
635 1e3463f1 Michael Hanselmann
636 1e3463f1 Michael Hanselmann
  storage_type = ConvertStorageType(user_storage_type)
637 1e3463f1 Michael Hanselmann
638 1e3463f1 Michael Hanselmann
  op = opcodes.OpRepairNodeStorage(node_name=node_name,
639 1e3463f1 Michael Hanselmann
                                   storage_type=storage_type,
640 7e9c6a78 Iustin Pop
                                   name=volume_name,
641 7e9c6a78 Iustin Pop
                                   ignore_consistency=opts.ignore_consistency)
642 400ca2f7 Iustin Pop
  SubmitOpCode(op, opts=opts)
643 1e3463f1 Michael Hanselmann
644 1e3463f1 Michael Hanselmann
645 b31c8676 Iustin Pop
def SetNodeParams(opts, args):
646 b31c8676 Iustin Pop
  """Modifies a node.
647 b31c8676 Iustin Pop
648 b31c8676 Iustin Pop
  @param opts: the command line options selected by the user
649 b31c8676 Iustin Pop
  @type args: list
650 b31c8676 Iustin Pop
  @param args: should contain only one element, the node name
651 b31c8676 Iustin Pop
  @rtype: int
652 b31c8676 Iustin Pop
  @return: the desired exit code
653 b31c8676 Iustin Pop
654 b31c8676 Iustin Pop
  """
655 c9d443ea Iustin Pop
  if [opts.master_candidate, opts.drained, opts.offline].count(None) == 3:
656 b31c8676 Iustin Pop
    ToStderr("Please give at least one of the parameters.")
657 b31c8676 Iustin Pop
    return 1
658 b31c8676 Iustin Pop
659 b31c8676 Iustin Pop
  op = opcodes.OpSetNodeParams(node_name=args[0],
660 e7b61bb0 Iustin Pop
                               master_candidate=opts.master_candidate,
661 e7b61bb0 Iustin Pop
                               offline=opts.offline,
662 e7b61bb0 Iustin Pop
                               drained=opts.drained,
663 4c61d894 Iustin Pop
                               force=opts.force,
664 4c61d894 Iustin Pop
                               auto_promote=opts.auto_promote)
665 b31c8676 Iustin Pop
666 b31c8676 Iustin Pop
  # even if here we process the result, we allow submit only
667 b31c8676 Iustin Pop
  result = SubmitOrSend(op, opts)
668 b31c8676 Iustin Pop
669 b31c8676 Iustin Pop
  if result:
670 b31c8676 Iustin Pop
    ToStdout("Modified node %s", args[0])
671 b31c8676 Iustin Pop
    for param, data in result:
672 b31c8676 Iustin Pop
      ToStdout(" - %-5s -> %s", param, data)
673 b31c8676 Iustin Pop
  return 0
674 b31c8676 Iustin Pop
675 b31c8676 Iustin Pop
676 a8083063 Iustin Pop
commands = {
677 6ea815cf Iustin Pop
  'add': (
678 6ea815cf Iustin Pop
    AddNode, [ArgHost(min=1, max=1)],
679 2e6469a1 René Nussbaumer
    [SECONDARY_IP_OPT, READD_OPT, NONODE_SETUP_OPT, VERBOSE_OPT],
680 2e6469a1 René Nussbaumer
    "[-s ip] [--readd] [--no-node-setup]  [--verbose] "
681 2e6469a1 René Nussbaumer
    " <node_name>",
682 6ea815cf Iustin Pop
    "Add a node to the cluster"),
683 6ea815cf Iustin Pop
  'evacuate': (
684 f8c9fa5c Iustin Pop
    EvacuateNode, [ArgNode(min=1)],
685 f8c9fa5c Iustin Pop
    [FORCE_OPT, IALLOCATOR_OPT, NEW_SECONDARY_OPT, EARLY_RELEASE_OPT],
686 6ea815cf Iustin Pop
    "[-f] {-I <iallocator> | -n <dst>} <node>",
687 6ea815cf Iustin Pop
    "Relocate the secondary instances from a node"
688 6ea815cf Iustin Pop
    " to other nodes (only for instances with drbd disk template)"),
689 6ea815cf Iustin Pop
  'failover': (
690 064c21f8 Iustin Pop
    FailoverNode, ARGS_ONE_NODE, [FORCE_OPT, IGNORE_CONSIST_OPT],
691 6ea815cf Iustin Pop
    "[-f] <node>",
692 6ea815cf Iustin Pop
    "Stops the primary instances on a node and start them on their"
693 6ea815cf Iustin Pop
    " secondary node (only for instances with drbd disk template)"),
694 6ea815cf Iustin Pop
  'migrate': (
695 783a6c0b Iustin Pop
    MigrateNode, ARGS_ONE_NODE, [FORCE_OPT, NONLIVE_OPT, MIGRATION_MODE_OPT],
696 6ea815cf Iustin Pop
    "[-f] <node>",
697 6ea815cf Iustin Pop
    "Migrate all the primary instance on a node away from it"
698 6ea815cf Iustin Pop
    " (only for instances of type drbd)"),
699 6ea815cf Iustin Pop
  'info': (
700 064c21f8 Iustin Pop
    ShowNodeConfig, ARGS_MANY_NODES, [],
701 6ea815cf Iustin Pop
    "[<node_name>...]", "Show information about the node(s)"),
702 6ea815cf Iustin Pop
  'list': (
703 6ea815cf Iustin Pop
    ListNodes, ARGS_MANY_NODES,
704 31d97b2a Guido Trotter
    [NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT, SYNC_OPT, ROMAN_OPT],
705 6ea815cf Iustin Pop
    "[nodes...]",
706 6ea815cf Iustin Pop
    "Lists the nodes in the cluster. The available fields are (see the man"
707 6ea815cf Iustin Pop
    " page for details): %s. The default field list is (in order): %s." %
708 1f864b60 Iustin Pop
    (utils.CommaJoin(_LIST_HEADERS), utils.CommaJoin(_LIST_DEF_FIELDS))),
709 6ea815cf Iustin Pop
  'modify': (
710 6ea815cf Iustin Pop
    SetNodeParams, ARGS_ONE_NODE,
711 4c61d894 Iustin Pop
    [FORCE_OPT, SUBMIT_OPT, MC_OPT, DRAINED_OPT, OFFLINE_OPT,
712 4c61d894 Iustin Pop
     AUTO_PROMOTE_OPT],
713 6ea815cf Iustin Pop
    "<node_name>", "Alters the parameters of a node"),
714 6ea815cf Iustin Pop
  'powercycle': (
715 6ea815cf Iustin Pop
    PowercycleNode, ARGS_ONE_NODE,
716 064c21f8 Iustin Pop
    [FORCE_OPT, CONFIRM_OPT],
717 6ea815cf Iustin Pop
    "<node_name>", "Tries to forcefully powercycle a node"),
718 6ea815cf Iustin Pop
  'remove': (
719 064c21f8 Iustin Pop
    RemoveNode, ARGS_ONE_NODE, [],
720 6ea815cf Iustin Pop
    "<node_name>", "Removes a node from the cluster"),
721 6ea815cf Iustin Pop
  'volumes': (
722 6ea815cf Iustin Pop
    ListVolumes, [ArgNode()],
723 064c21f8 Iustin Pop
    [NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT],
724 6ea815cf Iustin Pop
    "[<node_name>...]", "List logical volumes on node(s)"),
725 9b94905f Iustin Pop
  'list-storage': (
726 9b94905f Iustin Pop
    ListStorage, ARGS_MANY_NODES,
727 064c21f8 Iustin Pop
    [NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT, _STORAGE_TYPE_OPT],
728 620a85fd Iustin Pop
    "[<node_name>...]", "List physical volumes on node(s). The available"
729 620a85fd Iustin Pop
    " fields are (see the man page for details): %s." %
730 1f864b60 Iustin Pop
    (utils.CommaJoin(_LIST_STOR_HEADERS))),
731 9b94905f Iustin Pop
  'modify-storage': (
732 9b94905f Iustin Pop
    ModifyStorage,
733 6ea815cf Iustin Pop
    [ArgNode(min=1, max=1),
734 6ea815cf Iustin Pop
     ArgChoice(min=1, max=1, choices=_MODIFIABLE_STORAGE_TYPES),
735 6ea815cf Iustin Pop
     ArgFile(min=1, max=1)],
736 f7e41aa2 Iustin Pop
    [ALLOCATABLE_OPT],
737 064c21f8 Iustin Pop
    "<node_name> <storage_type> <name>", "Modify storage volume on a node"),
738 9b94905f Iustin Pop
  'repair-storage': (
739 9b94905f Iustin Pop
    RepairStorage,
740 6ea815cf Iustin Pop
    [ArgNode(min=1, max=1),
741 6ea815cf Iustin Pop
     ArgChoice(min=1, max=1, choices=_REPAIRABLE_STORAGE_TYPES),
742 6ea815cf Iustin Pop
     ArgFile(min=1, max=1)],
743 7e9c6a78 Iustin Pop
    [IGNORE_CONSIST_OPT],
744 6ea815cf Iustin Pop
    "<node_name> <storage_type> <name>",
745 6ea815cf Iustin Pop
    "Repairs a storage volume on a node"),
746 6ea815cf Iustin Pop
  'list-tags': (
747 064c21f8 Iustin Pop
    ListTags, ARGS_ONE_NODE, [],
748 6ea815cf Iustin Pop
    "<node_name>", "List the tags of the given node"),
749 6ea815cf Iustin Pop
  'add-tags': (
750 064c21f8 Iustin Pop
    AddTags, [ArgNode(min=1, max=1), ArgUnknown()], [TAG_SRC_OPT],
751 6ea815cf Iustin Pop
    "<node_name> tag...", "Add tags to the given node"),
752 6ea815cf Iustin Pop
  'remove-tags': (
753 064c21f8 Iustin Pop
    RemoveTags, [ArgNode(min=1, max=1), ArgUnknown()], [TAG_SRC_OPT],
754 6ea815cf Iustin Pop
    "<node_name> tag...", "Remove tags from the given node"),
755 a8083063 Iustin Pop
  }
756 a8083063 Iustin Pop
757 a8083063 Iustin Pop
758 a8083063 Iustin Pop
if __name__ == '__main__':
759 846baef9 Iustin Pop
  sys.exit(GenericMain(commands, override={"tag_type": constants.TAG_NODE}))