Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-node @ c54784d9

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