Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-node @ 154b9580

History | View | Annotate | Download (21.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 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 104f4ca1 Iustin Pop
  dns_data = utils.GetHostInfo(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 a8083063 Iustin Pop
  SubmitOpCode(op)
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 a5bc662a Iustin Pop
  selected_fields = ["name", "sinst_list"]
259 c4ed32cb Iustin Pop
  src_node = args[0]
260 a5bc662a Iustin Pop
261 ec79568d Iustin Pop
  result = cl.QueryNodes(names=[src_node], fields=selected_fields,
262 77921a95 Iustin Pop
                         use_locking=False)
263 a5bc662a Iustin Pop
  src_node, sinst = result[0]
264 a5bc662a Iustin Pop
265 a5bc662a Iustin Pop
  if not sinst:
266 3a24c527 Iustin Pop
    ToStderr("No secondary instances on node %s, exiting.", src_node)
267 a5bc662a Iustin Pop
    return constants.EXIT_SUCCESS
268 a5bc662a Iustin Pop
269 c4ed32cb Iustin Pop
  if dst_node is not None:
270 77921a95 Iustin Pop
    result = cl.QueryNodes(names=[dst_node], fields=["name"],
271 77921a95 Iustin Pop
                           use_locking=False)
272 c4ed32cb Iustin Pop
    dst_node = result[0][0]
273 c4ed32cb Iustin Pop
274 c4ed32cb Iustin Pop
    if src_node == dst_node:
275 c4ed32cb Iustin Pop
      raise errors.OpPrereqError("Evacuate node needs different source and"
276 c4ed32cb Iustin Pop
                                 " target nodes (node %s given twice)" %
277 debac808 Iustin Pop
                                 src_node, errors.ECODE_INVAL)
278 c4ed32cb Iustin Pop
    txt_msg = "to node %s" % dst_node
279 c4ed32cb Iustin Pop
  else:
280 c4ed32cb Iustin Pop
    txt_msg = "using iallocator %s" % iallocator
281 c4ed32cb Iustin Pop
282 a5bc662a Iustin Pop
  sinst = utils.NiceSort(sinst)
283 a5bc662a Iustin Pop
284 a5bc662a Iustin Pop
  if not force and not AskUser("Relocate instance(s) %s from node\n"
285 c4ed32cb Iustin Pop
                               " %s %s?" %
286 a5bc662a Iustin Pop
                               (",".join("'%s'" % name for name in sinst),
287 c4ed32cb Iustin Pop
                               src_node, txt_msg)):
288 a5bc662a Iustin Pop
    return constants.EXIT_CONFIRMATION
289 a5bc662a Iustin Pop
290 80dd50bf Michael Hanselmann
  op = opcodes.OpEvacuateNode(node_name=args[0], remote_node=dst_node,
291 80dd50bf Michael Hanselmann
                              iallocator=iallocator)
292 80dd50bf Michael Hanselmann
  SubmitOpCode(op, cl=cl)
293 a5bc662a Iustin Pop
294 a5bc662a Iustin Pop
295 c450e9b0 Iustin Pop
def FailoverNode(opts, args):
296 c450e9b0 Iustin Pop
  """Failover all primary instance on a node.
297 c450e9b0 Iustin Pop
298 ebf366ee Iustin Pop
  @param opts: the command line options selected by the user
299 ebf366ee Iustin Pop
  @type args: list
300 ebf366ee Iustin Pop
  @param args: should be an empty list
301 ebf366ee Iustin Pop
  @rtype: int
302 ebf366ee Iustin Pop
  @return: the desired exit code
303 ebf366ee Iustin Pop
304 c450e9b0 Iustin Pop
  """
305 479636a3 Iustin Pop
  cl = GetClient()
306 c450e9b0 Iustin Pop
  force = opts.force
307 c450e9b0 Iustin Pop
  selected_fields = ["name", "pinst_list"]
308 c450e9b0 Iustin Pop
309 2e7b8369 Iustin Pop
  # these fields are static data anyway, so it doesn't matter, but
310 2e7b8369 Iustin Pop
  # locking=True should be safer
311 2e7b8369 Iustin Pop
  result = cl.QueryNodes(names=args, fields=selected_fields,
312 77921a95 Iustin Pop
                         use_locking=False)
313 c450e9b0 Iustin Pop
  node, pinst = result[0]
314 c450e9b0 Iustin Pop
315 c450e9b0 Iustin Pop
  if not pinst:
316 3a24c527 Iustin Pop
    ToStderr("No primary instances on node %s, exiting.", node)
317 c450e9b0 Iustin Pop
    return 0
318 c450e9b0 Iustin Pop
319 c450e9b0 Iustin Pop
  pinst = utils.NiceSort(pinst)
320 c450e9b0 Iustin Pop
321 c450e9b0 Iustin Pop
  retcode = 0
322 c450e9b0 Iustin Pop
323 c450e9b0 Iustin Pop
  if not force and not AskUser("Fail over instance(s) %s?" %
324 c450e9b0 Iustin Pop
                               (",".join("'%s'" % name for name in pinst))):
325 c450e9b0 Iustin Pop
    return 2
326 c450e9b0 Iustin Pop
327 479636a3 Iustin Pop
  jex = JobExecutor(cl=cl)
328 c450e9b0 Iustin Pop
  for iname in pinst:
329 c450e9b0 Iustin Pop
    op = opcodes.OpFailoverInstance(instance_name=iname,
330 c450e9b0 Iustin Pop
                                    ignore_consistency=opts.ignore_consistency)
331 479636a3 Iustin Pop
    jex.QueueJob(iname, op)
332 479636a3 Iustin Pop
  results = jex.GetResults()
333 479636a3 Iustin Pop
  bad_cnt = len([row for row in results if not row[0]])
334 479636a3 Iustin Pop
  if bad_cnt == 0:
335 479636a3 Iustin Pop
    ToStdout("All %d instance(s) failed over successfully.", len(results))
336 c450e9b0 Iustin Pop
  else:
337 3a24c527 Iustin Pop
    ToStdout("There were errors during the failover:\n"
338 479636a3 Iustin Pop
             "%d error(s) out of %d instance(s).", bad_cnt, len(results))
339 c450e9b0 Iustin Pop
  return retcode
340 c450e9b0 Iustin Pop
341 c450e9b0 Iustin Pop
342 40ef0ed6 Iustin Pop
def MigrateNode(opts, args):
343 40ef0ed6 Iustin Pop
  """Migrate all primary instance on a node.
344 40ef0ed6 Iustin Pop
345 40ef0ed6 Iustin Pop
  """
346 40ef0ed6 Iustin Pop
  cl = GetClient()
347 40ef0ed6 Iustin Pop
  force = opts.force
348 40ef0ed6 Iustin Pop
  selected_fields = ["name", "pinst_list"]
349 40ef0ed6 Iustin Pop
350 77921a95 Iustin Pop
  result = cl.QueryNodes(names=args, fields=selected_fields, use_locking=False)
351 40ef0ed6 Iustin Pop
  node, pinst = result[0]
352 40ef0ed6 Iustin Pop
353 40ef0ed6 Iustin Pop
  if not pinst:
354 40ef0ed6 Iustin Pop
    ToStdout("No primary instances on node %s, exiting." % node)
355 40ef0ed6 Iustin Pop
    return 0
356 40ef0ed6 Iustin Pop
357 40ef0ed6 Iustin Pop
  pinst = utils.NiceSort(pinst)
358 40ef0ed6 Iustin Pop
359 40ef0ed6 Iustin Pop
  if not force and not AskUser("Migrate instance(s) %s?" %
360 40ef0ed6 Iustin Pop
                               (",".join("'%s'" % name for name in pinst))):
361 40ef0ed6 Iustin Pop
    return 2
362 40ef0ed6 Iustin Pop
363 b21d8c7f Michael Hanselmann
  op = opcodes.OpMigrateNode(node_name=args[0], live=opts.live)
364 b21d8c7f Michael Hanselmann
  SubmitOpCode(op, cl=cl)
365 40ef0ed6 Iustin Pop
366 40ef0ed6 Iustin Pop
367 a8083063 Iustin Pop
def ShowNodeConfig(opts, args):
368 a8083063 Iustin Pop
  """Show node information.
369 a8083063 Iustin Pop
370 ebf366ee Iustin Pop
  @param opts: the command line options selected by the user
371 ebf366ee Iustin Pop
  @type args: list
372 ebf366ee Iustin Pop
  @param args: should either be an empty list, in which case
373 ebf366ee Iustin Pop
      we show information about all nodes, or should contain
374 ebf366ee Iustin Pop
      a list of nodes to be queried for information
375 ebf366ee Iustin Pop
  @rtype: int
376 ebf366ee Iustin Pop
  @return: the desired exit code
377 ebf366ee Iustin Pop
378 a8083063 Iustin Pop
  """
379 2e7b8369 Iustin Pop
  cl = GetClient()
380 2e7b8369 Iustin Pop
  result = cl.QueryNodes(fields=["name", "pip", "sip",
381 0b2454b9 Iustin Pop
                                 "pinst_list", "sinst_list",
382 0b2454b9 Iustin Pop
                                 "master_candidate", "drained", "offline"],
383 77921a95 Iustin Pop
                         names=args, use_locking=False)
384 a8083063 Iustin Pop
385 0b2454b9 Iustin Pop
  for (name, primary_ip, secondary_ip, pinst, sinst,
386 0b2454b9 Iustin Pop
       is_mc, drained, offline) in result:
387 3a24c527 Iustin Pop
    ToStdout("Node name: %s", name)
388 3a24c527 Iustin Pop
    ToStdout("  primary ip: %s", primary_ip)
389 3a24c527 Iustin Pop
    ToStdout("  secondary ip: %s", secondary_ip)
390 0b2454b9 Iustin Pop
    ToStdout("  master candidate: %s", is_mc)
391 0b2454b9 Iustin Pop
    ToStdout("  drained: %s", drained)
392 0b2454b9 Iustin Pop
    ToStdout("  offline: %s", offline)
393 a8083063 Iustin Pop
    if pinst:
394 3a24c527 Iustin Pop
      ToStdout("  primary for instances:")
395 ae07a1d3 Iustin Pop
      for iname in utils.NiceSort(pinst):
396 3a24c527 Iustin Pop
        ToStdout("    - %s", iname)
397 a8083063 Iustin Pop
    else:
398 3a24c527 Iustin Pop
      ToStdout("  primary for no instances")
399 a8083063 Iustin Pop
    if sinst:
400 3a24c527 Iustin Pop
      ToStdout("  secondary for instances:")
401 ae07a1d3 Iustin Pop
      for iname in utils.NiceSort(sinst):
402 3a24c527 Iustin Pop
        ToStdout("    - %s", iname)
403 a8083063 Iustin Pop
    else:
404 3a24c527 Iustin Pop
      ToStdout("  secondary for no instances")
405 a8083063 Iustin Pop
406 a8083063 Iustin Pop
  return 0
407 a8083063 Iustin Pop
408 a8083063 Iustin Pop
409 a8083063 Iustin Pop
def RemoveNode(opts, args):
410 ebf366ee Iustin Pop
  """Remove a node from the cluster.
411 ebf366ee Iustin Pop
412 ebf366ee Iustin Pop
  @param opts: the command line options selected by the user
413 ebf366ee Iustin Pop
  @type args: list
414 ebf366ee Iustin Pop
  @param args: should contain only one element, the name of
415 ebf366ee Iustin Pop
      the node to be removed
416 ebf366ee Iustin Pop
  @rtype: int
417 ebf366ee Iustin Pop
  @return: the desired exit code
418 ebf366ee Iustin Pop
419 ebf366ee Iustin Pop
  """
420 a8083063 Iustin Pop
  op = opcodes.OpRemoveNode(node_name=args[0])
421 a8083063 Iustin Pop
  SubmitOpCode(op)
422 ebf366ee Iustin Pop
  return 0
423 a8083063 Iustin Pop
424 a8083063 Iustin Pop
425 f5118ade Iustin Pop
def PowercycleNode(opts, args):
426 f5118ade Iustin Pop
  """Remove a node from the cluster.
427 f5118ade Iustin Pop
428 f5118ade Iustin Pop
  @param opts: the command line options selected by the user
429 f5118ade Iustin Pop
  @type args: list
430 f5118ade Iustin Pop
  @param args: should contain only one element, the name of
431 f5118ade Iustin Pop
      the node to be removed
432 f5118ade Iustin Pop
  @rtype: int
433 f5118ade Iustin Pop
  @return: the desired exit code
434 f5118ade Iustin Pop
435 f5118ade Iustin Pop
  """
436 f5118ade Iustin Pop
  node = args[0]
437 f5118ade Iustin Pop
  if (not opts.confirm and
438 f5118ade Iustin Pop
      not AskUser("Are you sure you want to hard powercycle node %s?" % node)):
439 f5118ade Iustin Pop
    return 2
440 f5118ade Iustin Pop
441 f5118ade Iustin Pop
  op = opcodes.OpPowercycleNode(node_name=node, force=opts.force)
442 f5118ade Iustin Pop
  result = SubmitOpCode(op)
443 f5118ade Iustin Pop
  ToStderr(result)
444 f5118ade Iustin Pop
  return 0
445 f5118ade Iustin Pop
446 f5118ade Iustin Pop
447 dcb93971 Michael Hanselmann
def ListVolumes(opts, args):
448 dcb93971 Michael Hanselmann
  """List logical volumes on node(s).
449 dcb93971 Michael Hanselmann
450 ebf366ee Iustin Pop
  @param opts: the command line options selected by the user
451 ebf366ee Iustin Pop
  @type args: list
452 ebf366ee Iustin Pop
  @param args: should either be an empty list, in which case
453 ebf366ee Iustin Pop
      we list data for all nodes, or contain a list of nodes
454 ebf366ee Iustin Pop
      to display data only for those
455 ebf366ee Iustin Pop
  @rtype: int
456 ebf366ee Iustin Pop
  @return: the desired exit code
457 ebf366ee Iustin Pop
458 dcb93971 Michael Hanselmann
  """
459 dcb93971 Michael Hanselmann
  if opts.output is None:
460 dcb93971 Michael Hanselmann
    selected_fields = ["node", "phys", "vg",
461 dcb93971 Michael Hanselmann
                       "name", "size", "instance"]
462 dcb93971 Michael Hanselmann
  else:
463 dcb93971 Michael Hanselmann
    selected_fields = opts.output.split(",")
464 dcb93971 Michael Hanselmann
465 dcb93971 Michael Hanselmann
  op = opcodes.OpQueryNodeVolumes(nodes=args, output_fields=selected_fields)
466 dcb93971 Michael Hanselmann
  output = SubmitOpCode(op)
467 dcb93971 Michael Hanselmann
468 dcb93971 Michael Hanselmann
  if not opts.no_headers:
469 137161c9 Michael Hanselmann
    headers = {"node": "Node", "phys": "PhysDev",
470 137161c9 Michael Hanselmann
               "vg": "VG", "name": "Name",
471 137161c9 Michael Hanselmann
               "size": "Size", "instance": "Instance"}
472 137161c9 Michael Hanselmann
  else:
473 137161c9 Michael Hanselmann
    headers = None
474 137161c9 Michael Hanselmann
475 9fbfbb7b Iustin Pop
  unitfields = ["size"]
476 137161c9 Michael Hanselmann
477 137161c9 Michael Hanselmann
  numfields = ["size"]
478 137161c9 Michael Hanselmann
479 16be8703 Iustin Pop
  data = GenerateTable(separator=opts.separator, headers=headers,
480 16be8703 Iustin Pop
                       fields=selected_fields, unitfields=unitfields,
481 9fbfbb7b Iustin Pop
                       numfields=numfields, data=output, units=opts.units)
482 16be8703 Iustin Pop
483 16be8703 Iustin Pop
  for line in data:
484 3a24c527 Iustin Pop
    ToStdout(line)
485 dcb93971 Michael Hanselmann
486 dcb93971 Michael Hanselmann
  return 0
487 dcb93971 Michael Hanselmann
488 dcb93971 Michael Hanselmann
489 9b94905f Iustin Pop
def ListStorage(opts, args):
490 4007f57d Michael Hanselmann
  """List physical volumes on node(s).
491 4007f57d Michael Hanselmann
492 4007f57d Michael Hanselmann
  @param opts: the command line options selected by the user
493 4007f57d Michael Hanselmann
  @type args: list
494 4007f57d Michael Hanselmann
  @param args: should either be an empty list, in which case
495 4007f57d Michael Hanselmann
      we list data for all nodes, or contain a list of nodes
496 4007f57d Michael Hanselmann
      to display data only for those
497 4007f57d Michael Hanselmann
  @rtype: int
498 4007f57d Michael Hanselmann
  @return: the desired exit code
499 4007f57d Michael Hanselmann
500 4007f57d Michael Hanselmann
  """
501 53548798 Michael Hanselmann
  # TODO: Default to ST_FILE if LVM is disabled on the cluster
502 53548798 Michael Hanselmann
  if opts.user_storage_type is None:
503 53548798 Michael Hanselmann
    opts.user_storage_type = constants.ST_LVM_PV
504 53548798 Michael Hanselmann
505 86f5eae3 Michael Hanselmann
  storage_type = ConvertStorageType(opts.user_storage_type)
506 53548798 Michael Hanselmann
507 4007f57d Michael Hanselmann
  if opts.output is None:
508 620a85fd Iustin Pop
    selected_fields = _LIST_STOR_DEF_FIELDS
509 dc09c3cf Iustin Pop
  elif opts.output.startswith("+"):
510 620a85fd Iustin Pop
    selected_fields = _LIST_STOR_DEF_FIELDS + opts.output[1:].split(",")
511 4007f57d Michael Hanselmann
  else:
512 4007f57d Michael Hanselmann
    selected_fields = opts.output.split(",")
513 4007f57d Michael Hanselmann
514 4007f57d Michael Hanselmann
  op = opcodes.OpQueryNodeStorage(nodes=args,
515 53548798 Michael Hanselmann
                                  storage_type=storage_type,
516 4007f57d Michael Hanselmann
                                  output_fields=selected_fields)
517 4007f57d Michael Hanselmann
  output = SubmitOpCode(op)
518 4007f57d Michael Hanselmann
519 4007f57d Michael Hanselmann
  if not opts.no_headers:
520 4007f57d Michael Hanselmann
    headers = {
521 620a85fd Iustin Pop
      constants.SF_NODE: "Node",
522 620a85fd Iustin Pop
      constants.SF_TYPE: "Type",
523 4007f57d Michael Hanselmann
      constants.SF_NAME: "Name",
524 4007f57d Michael Hanselmann
      constants.SF_SIZE: "Size",
525 4007f57d Michael Hanselmann
      constants.SF_USED: "Used",
526 4007f57d Michael Hanselmann
      constants.SF_FREE: "Free",
527 4007f57d Michael Hanselmann
      constants.SF_ALLOCATABLE: "Allocatable",
528 4007f57d Michael Hanselmann
      }
529 4007f57d Michael Hanselmann
  else:
530 4007f57d Michael Hanselmann
    headers = None
531 4007f57d Michael Hanselmann
532 4007f57d Michael Hanselmann
  unitfields = [constants.SF_SIZE, constants.SF_USED, constants.SF_FREE]
533 4007f57d Michael Hanselmann
  numfields = [constants.SF_SIZE, constants.SF_USED, constants.SF_FREE]
534 4007f57d Michael Hanselmann
535 dc09c3cf Iustin Pop
  # change raw values to nicer strings
536 dc09c3cf Iustin Pop
  for row in output:
537 dc09c3cf Iustin Pop
    for idx, field in enumerate(selected_fields):
538 dc09c3cf Iustin Pop
      val = row[idx]
539 dc09c3cf Iustin Pop
      if field == constants.SF_ALLOCATABLE:
540 dc09c3cf Iustin Pop
        if val:
541 dc09c3cf Iustin Pop
          val = "Y"
542 dc09c3cf Iustin Pop
        else:
543 dc09c3cf Iustin Pop
          val = "N"
544 dc09c3cf Iustin Pop
      row[idx] = str(val)
545 dc09c3cf Iustin Pop
546 4007f57d Michael Hanselmann
  data = GenerateTable(separator=opts.separator, headers=headers,
547 4007f57d Michael Hanselmann
                       fields=selected_fields, unitfields=unitfields,
548 4007f57d Michael Hanselmann
                       numfields=numfields, data=output, units=opts.units)
549 4007f57d Michael Hanselmann
550 4007f57d Michael Hanselmann
  for line in data:
551 4007f57d Michael Hanselmann
    ToStdout(line)
552 4007f57d Michael Hanselmann
553 4007f57d Michael Hanselmann
  return 0
554 4007f57d Michael Hanselmann
555 4007f57d Michael Hanselmann
556 9b94905f Iustin Pop
def ModifyStorage(opts, args):
557 0e89fc2d Michael Hanselmann
  """Modify storage volume on a node.
558 0e89fc2d Michael Hanselmann
559 0e89fc2d Michael Hanselmann
  @param opts: the command line options selected by the user
560 0e89fc2d Michael Hanselmann
  @type args: list
561 0e89fc2d Michael Hanselmann
  @param args: should contain 3 items: node name, storage type and volume name
562 0e89fc2d Michael Hanselmann
  @rtype: int
563 0e89fc2d Michael Hanselmann
  @return: the desired exit code
564 0e89fc2d Michael Hanselmann
565 0e89fc2d Michael Hanselmann
  """
566 0e89fc2d Michael Hanselmann
  (node_name, user_storage_type, volume_name) = args
567 0e89fc2d Michael Hanselmann
568 86f5eae3 Michael Hanselmann
  storage_type = ConvertStorageType(user_storage_type)
569 0e89fc2d Michael Hanselmann
570 0e89fc2d Michael Hanselmann
  changes = {}
571 0e89fc2d Michael Hanselmann
572 0e89fc2d Michael Hanselmann
  if opts.allocatable is not None:
573 0e89fc2d Michael Hanselmann
    changes[constants.SF_ALLOCATABLE] = (opts.allocatable == "yes")
574 0e89fc2d Michael Hanselmann
575 0e89fc2d Michael Hanselmann
  if changes:
576 0e89fc2d Michael Hanselmann
    op = opcodes.OpModifyNodeStorage(node_name=node_name,
577 0e89fc2d Michael Hanselmann
                                     storage_type=storage_type,
578 0e89fc2d Michael Hanselmann
                                     name=volume_name,
579 0e89fc2d Michael Hanselmann
                                     changes=changes)
580 0e89fc2d Michael Hanselmann
    SubmitOpCode(op)
581 620a85fd Iustin Pop
  else:
582 620a85fd Iustin Pop
    ToStderr("No changes to perform, exiting.")
583 0e89fc2d Michael Hanselmann
584 0e89fc2d Michael Hanselmann
585 9b94905f Iustin Pop
def RepairStorage(opts, args):
586 1e3463f1 Michael Hanselmann
  """Repairs a storage volume on a node.
587 1e3463f1 Michael Hanselmann
588 1e3463f1 Michael Hanselmann
  @param opts: the command line options selected by the user
589 1e3463f1 Michael Hanselmann
  @type args: list
590 1e3463f1 Michael Hanselmann
  @param args: should contain 3 items: node name, storage type and volume name
591 1e3463f1 Michael Hanselmann
  @rtype: int
592 1e3463f1 Michael Hanselmann
  @return: the desired exit code
593 1e3463f1 Michael Hanselmann
594 1e3463f1 Michael Hanselmann
  """
595 1e3463f1 Michael Hanselmann
  (node_name, user_storage_type, volume_name) = args
596 1e3463f1 Michael Hanselmann
597 1e3463f1 Michael Hanselmann
  storage_type = ConvertStorageType(user_storage_type)
598 1e3463f1 Michael Hanselmann
599 1e3463f1 Michael Hanselmann
  op = opcodes.OpRepairNodeStorage(node_name=node_name,
600 1e3463f1 Michael Hanselmann
                                   storage_type=storage_type,
601 7e9c6a78 Iustin Pop
                                   name=volume_name,
602 7e9c6a78 Iustin Pop
                                   ignore_consistency=opts.ignore_consistency)
603 1e3463f1 Michael Hanselmann
  SubmitOpCode(op)
604 1e3463f1 Michael Hanselmann
605 1e3463f1 Michael Hanselmann
606 b31c8676 Iustin Pop
def SetNodeParams(opts, args):
607 b31c8676 Iustin Pop
  """Modifies a node.
608 b31c8676 Iustin Pop
609 b31c8676 Iustin Pop
  @param opts: the command line options selected by the user
610 b31c8676 Iustin Pop
  @type args: list
611 b31c8676 Iustin Pop
  @param args: should contain only one element, the node name
612 b31c8676 Iustin Pop
  @rtype: int
613 b31c8676 Iustin Pop
  @return: the desired exit code
614 b31c8676 Iustin Pop
615 b31c8676 Iustin Pop
  """
616 c9d443ea Iustin Pop
  if [opts.master_candidate, opts.drained, opts.offline].count(None) == 3:
617 b31c8676 Iustin Pop
    ToStderr("Please give at least one of the parameters.")
618 b31c8676 Iustin Pop
    return 1
619 b31c8676 Iustin Pop
620 3a5ba66a Iustin Pop
  if opts.master_candidate is not None:
621 3a5ba66a Iustin Pop
    candidate = opts.master_candidate == 'yes'
622 3a5ba66a Iustin Pop
  else:
623 3a5ba66a Iustin Pop
    candidate = None
624 3a5ba66a Iustin Pop
  if opts.offline is not None:
625 3a5ba66a Iustin Pop
    offline = opts.offline == 'yes'
626 3a5ba66a Iustin Pop
  else:
627 3a5ba66a Iustin Pop
    offline = None
628 c9d443ea Iustin Pop
629 c9d443ea Iustin Pop
  if opts.drained is not None:
630 c9d443ea Iustin Pop
    drained = opts.drained == 'yes'
631 c9d443ea Iustin Pop
  else:
632 c9d443ea Iustin Pop
    drained = None
633 b31c8676 Iustin Pop
  op = opcodes.OpSetNodeParams(node_name=args[0],
634 3a5ba66a Iustin Pop
                               master_candidate=candidate,
635 3a5ba66a Iustin Pop
                               offline=offline,
636 c9d443ea Iustin Pop
                               drained=drained,
637 3a5ba66a Iustin Pop
                               force=opts.force)
638 b31c8676 Iustin Pop
639 b31c8676 Iustin Pop
  # even if here we process the result, we allow submit only
640 b31c8676 Iustin Pop
  result = SubmitOrSend(op, opts)
641 b31c8676 Iustin Pop
642 b31c8676 Iustin Pop
  if result:
643 b31c8676 Iustin Pop
    ToStdout("Modified node %s", args[0])
644 b31c8676 Iustin Pop
    for param, data in result:
645 b31c8676 Iustin Pop
      ToStdout(" - %-5s -> %s", param, data)
646 b31c8676 Iustin Pop
  return 0
647 b31c8676 Iustin Pop
648 b31c8676 Iustin Pop
649 a8083063 Iustin Pop
commands = {
650 6ea815cf Iustin Pop
  'add': (
651 6ea815cf Iustin Pop
    AddNode, [ArgHost(min=1, max=1)],
652 064c21f8 Iustin Pop
    [SECONDARY_IP_OPT, READD_OPT, NOSSH_KEYCHECK_OPT],
653 6ea815cf Iustin Pop
    "[-s ip] [--readd] [--no-ssh-key-check] <node_name>",
654 6ea815cf Iustin Pop
    "Add a node to the cluster"),
655 6ea815cf Iustin Pop
  'evacuate': (
656 6ea815cf Iustin Pop
    EvacuateNode, ARGS_ONE_NODE,
657 064c21f8 Iustin Pop
    [FORCE_OPT, IALLOCATOR_OPT, NEW_SECONDARY_OPT],
658 6ea815cf Iustin Pop
    "[-f] {-I <iallocator> | -n <dst>} <node>",
659 6ea815cf Iustin Pop
    "Relocate the secondary instances from a node"
660 6ea815cf Iustin Pop
    " to other nodes (only for instances with drbd disk template)"),
661 6ea815cf Iustin Pop
  'failover': (
662 064c21f8 Iustin Pop
    FailoverNode, ARGS_ONE_NODE, [FORCE_OPT, IGNORE_CONSIST_OPT],
663 6ea815cf Iustin Pop
    "[-f] <node>",
664 6ea815cf Iustin Pop
    "Stops the primary instances on a node and start them on their"
665 6ea815cf Iustin Pop
    " secondary node (only for instances with drbd disk template)"),
666 6ea815cf Iustin Pop
  'migrate': (
667 064c21f8 Iustin Pop
    MigrateNode, ARGS_ONE_NODE, [FORCE_OPT, NONLIVE_OPT],
668 6ea815cf Iustin Pop
    "[-f] <node>",
669 6ea815cf Iustin Pop
    "Migrate all the primary instance on a node away from it"
670 6ea815cf Iustin Pop
    " (only for instances of type drbd)"),
671 6ea815cf Iustin Pop
  'info': (
672 064c21f8 Iustin Pop
    ShowNodeConfig, ARGS_MANY_NODES, [],
673 6ea815cf Iustin Pop
    "[<node_name>...]", "Show information about the node(s)"),
674 6ea815cf Iustin Pop
  'list': (
675 6ea815cf Iustin Pop
    ListNodes, ARGS_MANY_NODES,
676 064c21f8 Iustin Pop
    [NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT, SYNC_OPT],
677 6ea815cf Iustin Pop
    "[nodes...]",
678 6ea815cf Iustin Pop
    "Lists the nodes in the cluster. The available fields are (see the man"
679 6ea815cf Iustin Pop
    " page for details): %s. The default field list is (in order): %s." %
680 1f864b60 Iustin Pop
    (utils.CommaJoin(_LIST_HEADERS), utils.CommaJoin(_LIST_DEF_FIELDS))),
681 6ea815cf Iustin Pop
  'modify': (
682 6ea815cf Iustin Pop
    SetNodeParams, ARGS_ONE_NODE,
683 064c21f8 Iustin Pop
    [FORCE_OPT, SUBMIT_OPT, MC_OPT, DRAINED_OPT, OFFLINE_OPT],
684 6ea815cf Iustin Pop
    "<node_name>", "Alters the parameters of a node"),
685 6ea815cf Iustin Pop
  'powercycle': (
686 6ea815cf Iustin Pop
    PowercycleNode, ARGS_ONE_NODE,
687 064c21f8 Iustin Pop
    [FORCE_OPT, CONFIRM_OPT],
688 6ea815cf Iustin Pop
    "<node_name>", "Tries to forcefully powercycle a node"),
689 6ea815cf Iustin Pop
  'remove': (
690 064c21f8 Iustin Pop
    RemoveNode, ARGS_ONE_NODE, [],
691 6ea815cf Iustin Pop
    "<node_name>", "Removes a node from the cluster"),
692 6ea815cf Iustin Pop
  'volumes': (
693 6ea815cf Iustin Pop
    ListVolumes, [ArgNode()],
694 064c21f8 Iustin Pop
    [NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT],
695 6ea815cf Iustin Pop
    "[<node_name>...]", "List logical volumes on node(s)"),
696 9b94905f Iustin Pop
  'list-storage': (
697 9b94905f Iustin Pop
    ListStorage, ARGS_MANY_NODES,
698 064c21f8 Iustin Pop
    [NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT, _STORAGE_TYPE_OPT],
699 620a85fd Iustin Pop
    "[<node_name>...]", "List physical volumes on node(s). The available"
700 620a85fd Iustin Pop
    " fields are (see the man page for details): %s." %
701 1f864b60 Iustin Pop
    (utils.CommaJoin(_LIST_STOR_HEADERS))),
702 9b94905f Iustin Pop
  'modify-storage': (
703 9b94905f Iustin Pop
    ModifyStorage,
704 6ea815cf Iustin Pop
    [ArgNode(min=1, max=1),
705 6ea815cf Iustin Pop
     ArgChoice(min=1, max=1, choices=_MODIFIABLE_STORAGE_TYPES),
706 6ea815cf Iustin Pop
     ArgFile(min=1, max=1)],
707 f7e41aa2 Iustin Pop
    [ALLOCATABLE_OPT],
708 064c21f8 Iustin Pop
    "<node_name> <storage_type> <name>", "Modify storage volume on a node"),
709 9b94905f Iustin Pop
  'repair-storage': (
710 9b94905f Iustin Pop
    RepairStorage,
711 6ea815cf Iustin Pop
    [ArgNode(min=1, max=1),
712 6ea815cf Iustin Pop
     ArgChoice(min=1, max=1, choices=_REPAIRABLE_STORAGE_TYPES),
713 6ea815cf Iustin Pop
     ArgFile(min=1, max=1)],
714 7e9c6a78 Iustin Pop
    [IGNORE_CONSIST_OPT],
715 6ea815cf Iustin Pop
    "<node_name> <storage_type> <name>",
716 6ea815cf Iustin Pop
    "Repairs a storage volume on a node"),
717 6ea815cf Iustin Pop
  'list-tags': (
718 064c21f8 Iustin Pop
    ListTags, ARGS_ONE_NODE, [],
719 6ea815cf Iustin Pop
    "<node_name>", "List the tags of the given node"),
720 6ea815cf Iustin Pop
  'add-tags': (
721 064c21f8 Iustin Pop
    AddTags, [ArgNode(min=1, max=1), ArgUnknown()], [TAG_SRC_OPT],
722 6ea815cf Iustin Pop
    "<node_name> tag...", "Add tags to the given node"),
723 6ea815cf Iustin Pop
  'remove-tags': (
724 064c21f8 Iustin Pop
    RemoveTags, [ArgNode(min=1, max=1), ArgUnknown()], [TAG_SRC_OPT],
725 6ea815cf Iustin Pop
    "<node_name> tag...", "Remove tags from the given node"),
726 a8083063 Iustin Pop
  }
727 a8083063 Iustin Pop
728 a8083063 Iustin Pop
729 a8083063 Iustin Pop
if __name__ == '__main__':
730 846baef9 Iustin Pop
  sys.exit(GenericMain(commands, override={"tag_type": constants.TAG_NODE}))