Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-node @ 9b94905f

History | View | Annotate | Download (20.6 kB)

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