Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-node @ fdad8c4d

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