Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-node @ 2f79bd34

History | View | Annotate | Download (11.9 kB)

1 a8083063 Iustin Pop
#!/usr/bin/python
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 e7c6e02b Michael Hanselmann
# Copyright (C) 2006, 2007, 2008 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 a8083063 Iustin Pop
22 2f79bd34 Iustin Pop
# pylint: disable-msg=W0401,W0614
23 2f79bd34 Iustin Pop
# W0401: Wildcard import ganeti.cli
24 2f79bd34 Iustin Pop
# W0614: Unused import %s from wildcard import (since we need cli)
25 2f79bd34 Iustin Pop
26 a8083063 Iustin Pop
import sys
27 a8083063 Iustin Pop
from optparse import make_option
28 a8083063 Iustin Pop
29 a8083063 Iustin Pop
from ganeti.cli import *
30 a8083063 Iustin Pop
from ganeti import opcodes
31 a8083063 Iustin Pop
from ganeti import utils
32 846baef9 Iustin Pop
from ganeti import constants
33 c450e9b0 Iustin Pop
from ganeti import errors
34 827f753e Guido Trotter
from ganeti import bootstrap
35 a8083063 Iustin Pop
36 a8083063 Iustin Pop
37 48c4dfa8 Iustin Pop
_LIST_DEF_FIELDS = [
38 48c4dfa8 Iustin Pop
  "name", "dtotal", "dfree",
39 48c4dfa8 Iustin Pop
  "mtotal", "mnode", "mfree",
40 48c4dfa8 Iustin Pop
  "pinst_cnt", "sinst_cnt",
41 48c4dfa8 Iustin Pop
  ]
42 48c4dfa8 Iustin Pop
43 51144e33 Michael Hanselmann
44 a8083063 Iustin Pop
def AddNode(opts, args):
45 05ccd983 Guido Trotter
  """Add node cli-to-processor bridge.
46 05ccd983 Guido Trotter
47 05ccd983 Guido Trotter
  """
48 05ccd983 Guido Trotter
  dns_data = utils.HostInfo(args[0])
49 05ccd983 Guido Trotter
  node = dns_data.name
50 05ccd983 Guido Trotter
51 05ccd983 Guido Trotter
  if not opts.readd:
52 05ccd983 Guido Trotter
    op = opcodes.OpQueryNodes(output_fields=['name'], names=[node])
53 05ccd983 Guido Trotter
    try:
54 05ccd983 Guido Trotter
      output = SubmitOpCode(op)
55 05ccd983 Guido Trotter
    except (errors.OpPrereqError, errors.OpExecError):
56 05ccd983 Guido Trotter
      pass
57 05ccd983 Guido Trotter
    else:
58 3a24c527 Iustin Pop
      ToStderr("Node %s already in the cluster (as %s)"
59 3a24c527 Iustin Pop
               " - please use --readd", args[0], output[0][0])
60 05ccd983 Guido Trotter
      return 1
61 05ccd983 Guido Trotter
62 3a24c527 Iustin Pop
  ToStderr("-- WARNING -- \n"
63 3a24c527 Iustin Pop
           "Performing this operation is going to replace the ssh daemon"
64 3a24c527 Iustin Pop
           " keypair\n"
65 3a24c527 Iustin Pop
           "on the target machine (%s) with the ones of the"
66 3a24c527 Iustin Pop
           " current one\n"
67 3a24c527 Iustin Pop
           "and grant full intra-cluster ssh root access to/from it\n", node)
68 05ccd983 Guido Trotter
69 c4b6c29c Michael Hanselmann
  bootstrap.SetupNodeDaemon(node, opts.ssh_key_check)
70 827f753e Guido Trotter
71 e7c6e02b Michael Hanselmann
  op = opcodes.OpAddNode(node_name=args[0], secondary_ip=opts.secondary_ip,
72 e7c6e02b Michael Hanselmann
                         readd=opts.readd)
73 a8083063 Iustin Pop
  SubmitOpCode(op)
74 a8083063 Iustin Pop
75 a8083063 Iustin Pop
76 a8083063 Iustin Pop
def ListNodes(opts, args):
77 a8083063 Iustin Pop
  """List nodes and their properties.
78 a8083063 Iustin Pop
79 a8083063 Iustin Pop
  """
80 a8083063 Iustin Pop
  if opts.output is None:
81 48c4dfa8 Iustin Pop
    selected_fields = _LIST_DEF_FIELDS
82 48c4dfa8 Iustin Pop
  elif opts.output.startswith("+"):
83 48c4dfa8 Iustin Pop
    selected_fields = _LIST_DEF_FIELDS + opts.output[1:].split(",")
84 a8083063 Iustin Pop
  else:
85 a8083063 Iustin Pop
    selected_fields = opts.output.split(",")
86 a8083063 Iustin Pop
87 c54784d9 Michael Hanselmann
  output = GetClient().QueryNodes([], selected_fields)
88 a8083063 Iustin Pop
89 a8083063 Iustin Pop
  if not opts.no_headers:
90 e8a4c138 Iustin Pop
    headers = {
91 e8a4c138 Iustin Pop
      "name": "Node", "pinst_cnt": "Pinst", "sinst_cnt": "Sinst",
92 e8a4c138 Iustin Pop
      "pinst_list": "PriInstances", "sinst_list": "SecInstances",
93 e8a4c138 Iustin Pop
      "pip": "PrimaryIP", "sip": "SecondaryIP",
94 e8a4c138 Iustin Pop
      "dtotal": "DTotal", "dfree": "DFree",
95 e8a4c138 Iustin Pop
      "mtotal": "MTotal", "mnode": "MNode", "mfree": "MFree",
96 e8a4c138 Iustin Pop
      "bootid": "BootID",
97 e8a4c138 Iustin Pop
      "ctotal": "CTotal",
98 130a6a6f Iustin Pop
      "tags": "Tags",
99 38d7239a Iustin Pop
      "serial_no": "SerialNo",
100 e8a4c138 Iustin Pop
      }
101 137161c9 Michael Hanselmann
  else:
102 137161c9 Michael Hanselmann
    headers = None
103 137161c9 Michael Hanselmann
104 137161c9 Michael Hanselmann
  if opts.human_readable:
105 137161c9 Michael Hanselmann
    unitfields = ["dtotal", "dfree", "mtotal", "mnode", "mfree"]
106 137161c9 Michael Hanselmann
  else:
107 137161c9 Michael Hanselmann
    unitfields = None
108 137161c9 Michael Hanselmann
109 ec223efb Iustin Pop
  numfields = ["dtotal", "dfree",
110 ec223efb Iustin Pop
               "mtotal", "mnode", "mfree",
111 e8a4c138 Iustin Pop
               "pinst_cnt", "sinst_cnt",
112 38d7239a Iustin Pop
               "ctotal", "serial_no"]
113 ec223efb Iustin Pop
114 130a6a6f Iustin Pop
  list_type_fields = ("pinst_list", "sinst_list", "tags")
115 ec223efb Iustin Pop
  # change raw values to nicer strings
116 ec223efb Iustin Pop
  for row in output:
117 ec223efb Iustin Pop
    for idx, field in enumerate(selected_fields):
118 ec223efb Iustin Pop
      val = row[idx]
119 130a6a6f Iustin Pop
      if field in list_type_fields:
120 ec223efb Iustin Pop
        val = ",".join(val)
121 ec223efb Iustin Pop
      elif val is None:
122 ec223efb Iustin Pop
        val = "?"
123 ec223efb Iustin Pop
      row[idx] = str(val)
124 137161c9 Michael Hanselmann
125 16be8703 Iustin Pop
  data = GenerateTable(separator=opts.separator, headers=headers,
126 16be8703 Iustin Pop
                       fields=selected_fields, unitfields=unitfields,
127 16be8703 Iustin Pop
                       numfields=numfields, data=output)
128 16be8703 Iustin Pop
  for line in data:
129 3a24c527 Iustin Pop
    ToStdout(line)
130 a8083063 Iustin Pop
131 a8083063 Iustin Pop
  return 0
132 a8083063 Iustin Pop
133 a8083063 Iustin Pop
134 a5bc662a Iustin Pop
def EvacuateNode(opts, args):
135 a5bc662a Iustin Pop
  """Relocate all secondary instance from a node.
136 a5bc662a Iustin Pop
137 a5bc662a Iustin Pop
  """
138 a5bc662a Iustin Pop
  force = opts.force
139 a5bc662a Iustin Pop
  selected_fields = ["name", "sinst_list"]
140 a5bc662a Iustin Pop
  src_node, dst_node = args
141 a5bc662a Iustin Pop
142 a5bc662a Iustin Pop
  op = opcodes.OpQueryNodes(output_fields=selected_fields, names=[src_node])
143 a5bc662a Iustin Pop
  result = SubmitOpCode(op)
144 a5bc662a Iustin Pop
  src_node, sinst = result[0]
145 a5bc662a Iustin Pop
  op = opcodes.OpQueryNodes(output_fields=["name"], names=[dst_node])
146 a5bc662a Iustin Pop
  result = SubmitOpCode(op)
147 a5bc662a Iustin Pop
  dst_node = result[0][0]
148 a5bc662a Iustin Pop
149 a5bc662a Iustin Pop
  if src_node == dst_node:
150 a5bc662a Iustin Pop
    raise errors.OpPrereqError("Evacuate node needs different source and"
151 a5bc662a Iustin Pop
                               " target nodes (node %s given twice)" %
152 a5bc662a Iustin Pop
                               src_node)
153 a5bc662a Iustin Pop
154 a5bc662a Iustin Pop
  if not sinst:
155 3a24c527 Iustin Pop
    ToStderr("No secondary instances on node %s, exiting.", src_node)
156 a5bc662a Iustin Pop
    return constants.EXIT_SUCCESS
157 a5bc662a Iustin Pop
158 a5bc662a Iustin Pop
  sinst = utils.NiceSort(sinst)
159 a5bc662a Iustin Pop
160 a5bc662a Iustin Pop
  retcode = constants.EXIT_SUCCESS
161 a5bc662a Iustin Pop
162 a5bc662a Iustin Pop
  if not force and not AskUser("Relocate instance(s) %s from node\n"
163 a5bc662a Iustin Pop
                               " %s to node\n %s?" %
164 a5bc662a Iustin Pop
                               (",".join("'%s'" % name for name in sinst),
165 a5bc662a Iustin Pop
                               src_node, dst_node)):
166 a5bc662a Iustin Pop
    return constants.EXIT_CONFIRMATION
167 a5bc662a Iustin Pop
168 a5bc662a Iustin Pop
  good_cnt = bad_cnt = 0
169 a5bc662a Iustin Pop
  for iname in sinst:
170 a5bc662a Iustin Pop
    op = opcodes.OpReplaceDisks(instance_name=iname,
171 22d31e49 Michael Hanselmann
                                remote_node=dst_node,
172 22d31e49 Michael Hanselmann
                                mode=constants.REPLACE_DISK_ALL,
173 22d31e49 Michael Hanselmann
                                disks=["sda", "sdb"])
174 a5bc662a Iustin Pop
    try:
175 3a24c527 Iustin Pop
      ToStdout("Replacing disks for instance %s", iname)
176 a5bc662a Iustin Pop
      SubmitOpCode(op)
177 3a24c527 Iustin Pop
      ToStdout("Instance %s has been relocated", iname)
178 a5bc662a Iustin Pop
      good_cnt += 1
179 a5bc662a Iustin Pop
    except errors.GenericError, err:
180 a5bc662a Iustin Pop
      nret, msg = FormatError(err)
181 a5bc662a Iustin Pop
      retcode |= nret
182 3a24c527 Iustin Pop
      ToStderr("Error replacing disks for instance %s: %s", iname, msg)
183 a5bc662a Iustin Pop
      bad_cnt += 1
184 a5bc662a Iustin Pop
185 a5bc662a Iustin Pop
  if retcode == constants.EXIT_SUCCESS:
186 3a24c527 Iustin Pop
    ToStdout("All %d instance(s) relocated successfully.", good_cnt)
187 a5bc662a Iustin Pop
  else:
188 3a24c527 Iustin Pop
    ToStdout("There were errors during the relocation:\n"
189 3a24c527 Iustin Pop
             "%d error(s) out of %d instance(s).", bad_cnt, good_cnt + bad_cnt)
190 a5bc662a Iustin Pop
  return retcode
191 a5bc662a Iustin Pop
192 a5bc662a Iustin Pop
193 c450e9b0 Iustin Pop
def FailoverNode(opts, args):
194 c450e9b0 Iustin Pop
  """Failover all primary instance on a node.
195 c450e9b0 Iustin Pop
196 c450e9b0 Iustin Pop
  """
197 c450e9b0 Iustin Pop
  force = opts.force
198 c450e9b0 Iustin Pop
  selected_fields = ["name", "pinst_list"]
199 c450e9b0 Iustin Pop
200 c450e9b0 Iustin Pop
  op = opcodes.OpQueryNodes(output_fields=selected_fields, names=args)
201 c450e9b0 Iustin Pop
  result = SubmitOpCode(op)
202 c450e9b0 Iustin Pop
  node, pinst = result[0]
203 c450e9b0 Iustin Pop
204 c450e9b0 Iustin Pop
  if not pinst:
205 3a24c527 Iustin Pop
    ToStderr("No primary instances on node %s, exiting.", node)
206 c450e9b0 Iustin Pop
    return 0
207 c450e9b0 Iustin Pop
208 c450e9b0 Iustin Pop
  pinst = utils.NiceSort(pinst)
209 c450e9b0 Iustin Pop
210 c450e9b0 Iustin Pop
  retcode = 0
211 c450e9b0 Iustin Pop
212 c450e9b0 Iustin Pop
  if not force and not AskUser("Fail over instance(s) %s?" %
213 c450e9b0 Iustin Pop
                               (",".join("'%s'" % name for name in pinst))):
214 c450e9b0 Iustin Pop
    return 2
215 c450e9b0 Iustin Pop
216 c450e9b0 Iustin Pop
  good_cnt = bad_cnt = 0
217 c450e9b0 Iustin Pop
  for iname in pinst:
218 c450e9b0 Iustin Pop
    op = opcodes.OpFailoverInstance(instance_name=iname,
219 c450e9b0 Iustin Pop
                                    ignore_consistency=opts.ignore_consistency)
220 c450e9b0 Iustin Pop
    try:
221 3a24c527 Iustin Pop
      ToStdout("Failing over instance %s", iname)
222 c450e9b0 Iustin Pop
      SubmitOpCode(op)
223 3a24c527 Iustin Pop
      ToStdout("Instance %s has been failed over", iname)
224 c450e9b0 Iustin Pop
      good_cnt += 1
225 c450e9b0 Iustin Pop
    except errors.GenericError, err:
226 c450e9b0 Iustin Pop
      nret, msg = FormatError(err)
227 c450e9b0 Iustin Pop
      retcode |= nret
228 3a24c527 Iustin Pop
      ToStderr("Error failing over instance %s: %s", iname, msg)
229 c450e9b0 Iustin Pop
      bad_cnt += 1
230 c450e9b0 Iustin Pop
231 c450e9b0 Iustin Pop
  if retcode == 0:
232 3a24c527 Iustin Pop
    ToStdout("All %d instance(s) failed over successfully.", good_cnt)
233 c450e9b0 Iustin Pop
  else:
234 3a24c527 Iustin Pop
    ToStdout("There were errors during the failover:\n"
235 3a24c527 Iustin Pop
             "%d error(s) out of %d instance(s).", bad_cnt, good_cnt + bad_cnt)
236 c450e9b0 Iustin Pop
  return retcode
237 c450e9b0 Iustin Pop
238 c450e9b0 Iustin Pop
239 a8083063 Iustin Pop
def ShowNodeConfig(opts, args):
240 a8083063 Iustin Pop
  """Show node information.
241 a8083063 Iustin Pop
242 a8083063 Iustin Pop
  """
243 4a72cc75 Iustin Pop
  op = opcodes.OpQueryNodes(output_fields=["name", "pip", "sip",
244 4a72cc75 Iustin Pop
                                           "pinst_list", "sinst_list"],
245 246e180a Iustin Pop
                            names=args)
246 a8083063 Iustin Pop
  result = SubmitOpCode(op)
247 a8083063 Iustin Pop
248 a8083063 Iustin Pop
  for name, primary_ip, secondary_ip, pinst, sinst in result:
249 3a24c527 Iustin Pop
    ToStdout("Node name: %s", name)
250 3a24c527 Iustin Pop
    ToStdout("  primary ip: %s", primary_ip)
251 3a24c527 Iustin Pop
    ToStdout("  secondary ip: %s", secondary_ip)
252 a8083063 Iustin Pop
    if pinst:
253 3a24c527 Iustin Pop
      ToStdout("  primary for instances:")
254 a8083063 Iustin Pop
      for iname in pinst:
255 3a24c527 Iustin Pop
        ToStdout("    - %s", iname)
256 a8083063 Iustin Pop
    else:
257 3a24c527 Iustin Pop
      ToStdout("  primary for no instances")
258 a8083063 Iustin Pop
    if sinst:
259 3a24c527 Iustin Pop
      ToStdout("  secondary for instances:")
260 a8083063 Iustin Pop
      for iname in sinst:
261 3a24c527 Iustin Pop
        ToStdout("    - %s", iname)
262 a8083063 Iustin Pop
    else:
263 3a24c527 Iustin Pop
      ToStdout("  secondary for no instances")
264 a8083063 Iustin Pop
265 a8083063 Iustin Pop
  return 0
266 a8083063 Iustin Pop
267 a8083063 Iustin Pop
268 a8083063 Iustin Pop
def RemoveNode(opts, args):
269 a8083063 Iustin Pop
  """Remove node cli-to-processor bridge."""
270 a8083063 Iustin Pop
  op = opcodes.OpRemoveNode(node_name=args[0])
271 a8083063 Iustin Pop
  SubmitOpCode(op)
272 a8083063 Iustin Pop
273 a8083063 Iustin Pop
274 dcb93971 Michael Hanselmann
def ListVolumes(opts, args):
275 dcb93971 Michael Hanselmann
  """List logical volumes on node(s).
276 dcb93971 Michael Hanselmann
277 dcb93971 Michael Hanselmann
  """
278 dcb93971 Michael Hanselmann
  if opts.output is None:
279 dcb93971 Michael Hanselmann
    selected_fields = ["node", "phys", "vg",
280 dcb93971 Michael Hanselmann
                       "name", "size", "instance"]
281 dcb93971 Michael Hanselmann
  else:
282 dcb93971 Michael Hanselmann
    selected_fields = opts.output.split(",")
283 dcb93971 Michael Hanselmann
284 dcb93971 Michael Hanselmann
  op = opcodes.OpQueryNodeVolumes(nodes=args, output_fields=selected_fields)
285 dcb93971 Michael Hanselmann
  output = SubmitOpCode(op)
286 dcb93971 Michael Hanselmann
287 dcb93971 Michael Hanselmann
  if not opts.no_headers:
288 137161c9 Michael Hanselmann
    headers = {"node": "Node", "phys": "PhysDev",
289 137161c9 Michael Hanselmann
               "vg": "VG", "name": "Name",
290 137161c9 Michael Hanselmann
               "size": "Size", "instance": "Instance"}
291 137161c9 Michael Hanselmann
  else:
292 137161c9 Michael Hanselmann
    headers = None
293 137161c9 Michael Hanselmann
294 137161c9 Michael Hanselmann
  if opts.human_readable:
295 137161c9 Michael Hanselmann
    unitfields = ["size"]
296 137161c9 Michael Hanselmann
  else:
297 137161c9 Michael Hanselmann
    unitfields = None
298 137161c9 Michael Hanselmann
299 137161c9 Michael Hanselmann
  numfields = ["size"]
300 137161c9 Michael Hanselmann
301 16be8703 Iustin Pop
  data = GenerateTable(separator=opts.separator, headers=headers,
302 16be8703 Iustin Pop
                       fields=selected_fields, unitfields=unitfields,
303 16be8703 Iustin Pop
                       numfields=numfields, data=output)
304 16be8703 Iustin Pop
305 16be8703 Iustin Pop
  for line in data:
306 3a24c527 Iustin Pop
    ToStdout(line)
307 dcb93971 Michael Hanselmann
308 dcb93971 Michael Hanselmann
  return 0
309 dcb93971 Michael Hanselmann
310 dcb93971 Michael Hanselmann
311 a8083063 Iustin Pop
commands = {
312 a8083063 Iustin Pop
  'add': (AddNode, ARGS_ONE,
313 a8083063 Iustin Pop
          [DEBUG_OPT,
314 a8083063 Iustin Pop
           make_option("-s", "--secondary-ip", dest="secondary_ip",
315 a8083063 Iustin Pop
                       help="Specify the secondary ip for the node",
316 e7c6e02b Michael Hanselmann
                       metavar="ADDRESS", default=None),
317 e7c6e02b Michael Hanselmann
           make_option("--readd", dest="readd",
318 e7c6e02b Michael Hanselmann
                       default=False, action="store_true",
319 e7c6e02b Michael Hanselmann
                       help="Readd old node after replacing it"),
320 c4b6c29c Michael Hanselmann
           make_option("--no-ssh-key-check", dest="ssh_key_check",
321 c4b6c29c Michael Hanselmann
                       default=True, action="store_false",
322 c4b6c29c Michael Hanselmann
                       help="Disable SSH key fingerprint checking"),
323 e7c6e02b Michael Hanselmann
           ],
324 c4b6c29c Michael Hanselmann
          "[-s ip] [--readd] [--no-ssh-key-check] <node_name>",
325 c4b6c29c Michael Hanselmann
          "Add a node to the cluster"),
326 a5bc662a Iustin Pop
  'evacuate': (EvacuateNode, ARGS_FIXED(2),
327 a5bc662a Iustin Pop
               [DEBUG_OPT, FORCE_OPT],
328 9a033156 Iustin Pop
               "[-f] <src> <dst>",
329 a5bc662a Iustin Pop
               "Relocate the secondary instances from the first node"
330 abdf0113 Iustin Pop
               " to the second one (only for instances with drbd disk template"
331 abdf0113 Iustin Pop
               ),
332 c450e9b0 Iustin Pop
  'failover': (FailoverNode, ARGS_ONE,
333 c450e9b0 Iustin Pop
               [DEBUG_OPT, FORCE_OPT,
334 c450e9b0 Iustin Pop
                make_option("--ignore-consistency", dest="ignore_consistency",
335 c450e9b0 Iustin Pop
                            action="store_true", default=False,
336 c450e9b0 Iustin Pop
                            help="Ignore the consistency of the disks on"
337 c450e9b0 Iustin Pop
                            " the secondary"),
338 c450e9b0 Iustin Pop
                ],
339 9a033156 Iustin Pop
               "[-f] <node>",
340 c450e9b0 Iustin Pop
               "Stops the primary instances on a node and start them on their"
341 abdf0113 Iustin Pop
               " secondary node (only for instances with drbd disk template)"),
342 a8083063 Iustin Pop
  'info': (ShowNodeConfig, ARGS_ANY, [DEBUG_OPT],
343 9a033156 Iustin Pop
           "[<node_name>...]", "Show information about the node(s)"),
344 a8083063 Iustin Pop
  'list': (ListNodes, ARGS_NONE,
345 94428652 Iustin Pop
           [DEBUG_OPT, NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT,
346 94428652 Iustin Pop
            SUBMIT_OPT],
347 9a033156 Iustin Pop
           "", "Lists the nodes in the cluster. The available fields"
348 d8a4b51d Iustin Pop
           " are (see the man page for details): name, pinst_cnt, pinst_list,"
349 d8a4b51d Iustin Pop
           " sinst_cnt, sinst_list, pip, sip, dtotal, dfree, mtotal, mnode,"
350 38d7239a Iustin Pop
           " mfree, bootid, cpu_count, serial_no."
351 38d7239a Iustin Pop
           " The default field list is"
352 48c4dfa8 Iustin Pop
           " (in order): %s." % ", ".join(_LIST_DEF_FIELDS),
353 48c4dfa8 Iustin Pop
           ),
354 a8083063 Iustin Pop
  'remove': (RemoveNode, ARGS_ONE, [DEBUG_OPT],
355 9a033156 Iustin Pop
             "<node_name>", "Removes a node from the cluster"),
356 dcb93971 Michael Hanselmann
  'volumes': (ListVolumes, ARGS_ANY,
357 dcb93971 Michael Hanselmann
              [DEBUG_OPT, NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT],
358 9a033156 Iustin Pop
              "[<node_name>...]", "List logical volumes on node(s)"),
359 846baef9 Iustin Pop
  'list-tags': (ListTags, ARGS_ONE, [DEBUG_OPT],
360 9a033156 Iustin Pop
                "<node_name>", "List the tags of the given node"),
361 810c50b7 Iustin Pop
  'add-tags': (AddTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
362 9a033156 Iustin Pop
               "<node_name> tag...", "Add tags to the given node"),
363 810c50b7 Iustin Pop
  'remove-tags': (RemoveTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
364 9a033156 Iustin Pop
                  "<node_name> tag...", "Remove tags from the given node"),
365 a8083063 Iustin Pop
  }
366 a8083063 Iustin Pop
367 a8083063 Iustin Pop
368 a8083063 Iustin Pop
if __name__ == '__main__':
369 846baef9 Iustin Pop
  sys.exit(GenericMain(commands, override={"tag_type": constants.TAG_NODE}))