Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-node @ a8ae3eb5

History | View | Annotate | Download (18.2 kB)

1 a8083063 Iustin Pop
#!/usr/bin/python
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 e7c6e02b Michael Hanselmann
# Copyright (C) 2006, 2007, 2008 Google Inc.
5 a8083063 Iustin Pop
#
6 a8083063 Iustin Pop
# This program is free software; you can redistribute it and/or modify
7 a8083063 Iustin Pop
# it under the terms of the GNU General Public License as published by
8 a8083063 Iustin Pop
# the Free Software Foundation; either version 2 of the License, or
9 a8083063 Iustin Pop
# (at your option) any later version.
10 a8083063 Iustin Pop
#
11 a8083063 Iustin Pop
# This program is distributed in the hope that it will be useful, but
12 a8083063 Iustin Pop
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 a8083063 Iustin Pop
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 a8083063 Iustin Pop
# General Public License for more details.
15 a8083063 Iustin Pop
#
16 a8083063 Iustin Pop
# You should have received a copy of the GNU General Public License
17 a8083063 Iustin Pop
# along with this program; if not, write to the Free Software
18 a8083063 Iustin Pop
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 a8083063 Iustin Pop
# 02110-1301, USA.
20 a8083063 Iustin Pop
21 a8083063 Iustin Pop
22 2f79bd34 Iustin Pop
# pylint: disable-msg=W0401,W0614
23 2f79bd34 Iustin Pop
# W0401: Wildcard import ganeti.cli
24 2f79bd34 Iustin Pop
# W0614: Unused import %s from wildcard import (since we need cli)
25 2f79bd34 Iustin Pop
26 a8083063 Iustin Pop
import sys
27 a8083063 Iustin Pop
from optparse import make_option
28 a8083063 Iustin Pop
29 a8083063 Iustin Pop
from ganeti.cli import *
30 c4ed32cb Iustin Pop
from ganeti import cli
31 a8083063 Iustin Pop
from ganeti import opcodes
32 a8083063 Iustin Pop
from ganeti import utils
33 846baef9 Iustin Pop
from ganeti import constants
34 c450e9b0 Iustin Pop
from ganeti import errors
35 827f753e Guido Trotter
from ganeti import bootstrap
36 a8083063 Iustin Pop
37 a8083063 Iustin Pop
38 ebf366ee Iustin Pop
#: default list of field for L{ListNodes}
39 48c4dfa8 Iustin Pop
_LIST_DEF_FIELDS = [
40 48c4dfa8 Iustin Pop
  "name", "dtotal", "dfree",
41 48c4dfa8 Iustin Pop
  "mtotal", "mnode", "mfree",
42 48c4dfa8 Iustin Pop
  "pinst_cnt", "sinst_cnt",
43 48c4dfa8 Iustin Pop
  ]
44 48c4dfa8 Iustin Pop
45 cc3bcec8 Guido Trotter
#: headers (and full field list for L{ListNodes}
46 cc3bcec8 Guido Trotter
_LIST_HEADERS = {
47 cc3bcec8 Guido Trotter
  "name": "Node", "pinst_cnt": "Pinst", "sinst_cnt": "Sinst",
48 cc3bcec8 Guido Trotter
  "pinst_list": "PriInstances", "sinst_list": "SecInstances",
49 cc3bcec8 Guido Trotter
  "pip": "PrimaryIP", "sip": "SecondaryIP",
50 cc3bcec8 Guido Trotter
  "dtotal": "DTotal", "dfree": "DFree",
51 cc3bcec8 Guido Trotter
  "mtotal": "MTotal", "mnode": "MNode", "mfree": "MFree",
52 cc3bcec8 Guido Trotter
  "bootid": "BootID",
53 0105bad3 Iustin Pop
  "ctotal": "CTotal", "cnodes": "CNodes", "csockets": "CSockets",
54 cc3bcec8 Guido Trotter
  "tags": "Tags",
55 cc3bcec8 Guido Trotter
  "serial_no": "SerialNo",
56 cc3bcec8 Guido Trotter
  "master_candidate": "MasterC",
57 cc3bcec8 Guido Trotter
  "master": "IsMaster",
58 0b2454b9 Iustin Pop
  "offline": "Offline", "drained": "Drained",
59 c120ff34 Iustin Pop
  "role": "Role",
60 cc3bcec8 Guido Trotter
  }
61 cc3bcec8 Guido Trotter
62 51144e33 Michael Hanselmann
63 4331f6cd Michael Hanselmann
@UsesRPC
64 a8083063 Iustin Pop
def AddNode(opts, args):
65 ebf366ee Iustin Pop
  """Add a node to the cluster.
66 ebf366ee Iustin Pop
67 ebf366ee Iustin Pop
  @param opts: the command line options selected by the user
68 ebf366ee Iustin Pop
  @type args: list
69 ebf366ee Iustin Pop
  @param args: should contain only one element, the new node name
70 ebf366ee Iustin Pop
  @rtype: int
71 ebf366ee Iustin Pop
  @return: the desired exit code
72 05ccd983 Guido Trotter
73 05ccd983 Guido Trotter
  """
74 87622829 Iustin Pop
  cl = GetClient()
75 05ccd983 Guido Trotter
  dns_data = utils.HostInfo(args[0])
76 05ccd983 Guido Trotter
  node = dns_data.name
77 82e12743 Iustin Pop
  readd = opts.readd
78 82e12743 Iustin Pop
79 82e12743 Iustin Pop
  try:
80 82e12743 Iustin Pop
    output = cl.QueryNodes(names=[node], fields=['name', 'sip'],
81 77921a95 Iustin Pop
                           use_locking=False)
82 82e12743 Iustin Pop
    node_exists, sip = output[0]
83 82e12743 Iustin Pop
  except (errors.OpPrereqError, errors.OpExecError):
84 82e12743 Iustin Pop
    node_exists = ""
85 82e12743 Iustin Pop
    sip = None
86 82e12743 Iustin Pop
87 82e12743 Iustin Pop
  if readd:
88 82e12743 Iustin Pop
    if not node_exists:
89 82e12743 Iustin Pop
      ToStderr("Node %s not in the cluster"
90 82e12743 Iustin Pop
               " - please retry without '--readd'", node)
91 82e12743 Iustin Pop
      return 1
92 82e12743 Iustin Pop
  else:
93 82e12743 Iustin Pop
    if node_exists:
94 3a24c527 Iustin Pop
      ToStderr("Node %s already in the cluster (as %s)"
95 82e12743 Iustin Pop
               " - please retry with '--readd'", node, node_exists)
96 05ccd983 Guido Trotter
      return 1
97 82e12743 Iustin Pop
    sip = opts.secondary_ip
98 05ccd983 Guido Trotter
99 87622829 Iustin Pop
  # read the cluster name from the master
100 87622829 Iustin Pop
  output = cl.QueryConfigValues(['cluster_name'])
101 87622829 Iustin Pop
  cluster_name = output[0]
102 87622829 Iustin Pop
103 a8ae3eb5 Iustin Pop
  if not readd:
104 82e12743 Iustin Pop
    ToStderr("-- WARNING -- \n"
105 82e12743 Iustin Pop
             "Performing this operation is going to replace the ssh daemon"
106 82e12743 Iustin Pop
             " keypair\n"
107 82e12743 Iustin Pop
             "on the target machine (%s) with the ones of the"
108 82e12743 Iustin Pop
             " current one\n"
109 82e12743 Iustin Pop
             "and grant full intra-cluster ssh root access to/from it\n", node)
110 05ccd983 Guido Trotter
111 87622829 Iustin Pop
  bootstrap.SetupNodeDaemon(cluster_name, node, opts.ssh_key_check)
112 827f753e Guido Trotter
113 82e12743 Iustin Pop
  op = opcodes.OpAddNode(node_name=args[0], secondary_ip=sip,
114 e7c6e02b Michael Hanselmann
                         readd=opts.readd)
115 a8083063 Iustin Pop
  SubmitOpCode(op)
116 a8083063 Iustin Pop
117 a8083063 Iustin Pop
118 a8083063 Iustin Pop
def ListNodes(opts, args):
119 a8083063 Iustin Pop
  """List nodes and their properties.
120 a8083063 Iustin Pop
121 ebf366ee Iustin Pop
  @param opts: the command line options selected by the user
122 ebf366ee Iustin Pop
  @type args: list
123 ebf366ee Iustin Pop
  @param args: should be an empty list
124 ebf366ee Iustin Pop
  @rtype: int
125 ebf366ee Iustin Pop
  @return: the desired exit code
126 ebf366ee Iustin Pop
127 a8083063 Iustin Pop
  """
128 a8083063 Iustin Pop
  if opts.output is None:
129 48c4dfa8 Iustin Pop
    selected_fields = _LIST_DEF_FIELDS
130 48c4dfa8 Iustin Pop
  elif opts.output.startswith("+"):
131 48c4dfa8 Iustin Pop
    selected_fields = _LIST_DEF_FIELDS + opts.output[1:].split(",")
132 a8083063 Iustin Pop
  else:
133 a8083063 Iustin Pop
    selected_fields = opts.output.split(",")
134 a8083063 Iustin Pop
135 f1de3563 Iustin Pop
  output = GetClient().QueryNodes(args, selected_fields, opts.do_locking)
136 a8083063 Iustin Pop
137 a8083063 Iustin Pop
  if not opts.no_headers:
138 cc3bcec8 Guido Trotter
    headers = _LIST_HEADERS
139 137161c9 Michael Hanselmann
  else:
140 137161c9 Michael Hanselmann
    headers = None
141 137161c9 Michael Hanselmann
142 9fbfbb7b Iustin Pop
  unitfields = ["dtotal", "dfree", "mtotal", "mnode", "mfree"]
143 137161c9 Michael Hanselmann
144 ec223efb Iustin Pop
  numfields = ["dtotal", "dfree",
145 ec223efb Iustin Pop
               "mtotal", "mnode", "mfree",
146 e8a4c138 Iustin Pop
               "pinst_cnt", "sinst_cnt",
147 38d7239a Iustin Pop
               "ctotal", "serial_no"]
148 ec223efb Iustin Pop
149 130a6a6f Iustin Pop
  list_type_fields = ("pinst_list", "sinst_list", "tags")
150 ec223efb Iustin Pop
  # change raw values to nicer strings
151 ec223efb Iustin Pop
  for row in output:
152 ec223efb Iustin Pop
    for idx, field in enumerate(selected_fields):
153 ec223efb Iustin Pop
      val = row[idx]
154 130a6a6f Iustin Pop
      if field in list_type_fields:
155 ec223efb Iustin Pop
        val = ",".join(val)
156 0b2454b9 Iustin Pop
      elif field in ('master', 'master_candidate', 'offline', 'drained'):
157 0e67cdbe Iustin Pop
        if val:
158 0e67cdbe Iustin Pop
          val = 'Y'
159 0e67cdbe Iustin Pop
        else:
160 0e67cdbe Iustin Pop
          val = 'N'
161 ec223efb Iustin Pop
      elif val is None:
162 ec223efb Iustin Pop
        val = "?"
163 ec223efb Iustin Pop
      row[idx] = str(val)
164 137161c9 Michael Hanselmann
165 16be8703 Iustin Pop
  data = GenerateTable(separator=opts.separator, headers=headers,
166 16be8703 Iustin Pop
                       fields=selected_fields, unitfields=unitfields,
167 9fbfbb7b Iustin Pop
                       numfields=numfields, data=output, units=opts.units)
168 16be8703 Iustin Pop
  for line in data:
169 3a24c527 Iustin Pop
    ToStdout(line)
170 a8083063 Iustin Pop
171 a8083063 Iustin Pop
  return 0
172 a8083063 Iustin Pop
173 a8083063 Iustin Pop
174 a5bc662a Iustin Pop
def EvacuateNode(opts, args):
175 a5bc662a Iustin Pop
  """Relocate all secondary instance from a node.
176 a5bc662a Iustin Pop
177 ebf366ee Iustin Pop
  @param opts: the command line options selected by the user
178 ebf366ee Iustin Pop
  @type args: list
179 ebf366ee Iustin Pop
  @param args: should be an empty list
180 ebf366ee Iustin Pop
  @rtype: int
181 ebf366ee Iustin Pop
  @return: the desired exit code
182 ebf366ee Iustin Pop
183 a5bc662a Iustin Pop
  """
184 479636a3 Iustin Pop
  cl = GetClient()
185 a5bc662a Iustin Pop
  force = opts.force
186 c4ed32cb Iustin Pop
187 c4ed32cb Iustin Pop
  dst_node = opts.dst_node
188 c4ed32cb Iustin Pop
  iallocator = opts.iallocator
189 c4ed32cb Iustin Pop
190 c4ed32cb Iustin Pop
  cnt = [dst_node, iallocator].count(None)
191 c4ed32cb Iustin Pop
  if cnt != 1:
192 c4ed32cb Iustin Pop
    raise errors.OpPrereqError("One and only one of the -n and -i"
193 c4ed32cb Iustin Pop
                               " options must be passed")
194 c4ed32cb Iustin Pop
195 a5bc662a Iustin Pop
  selected_fields = ["name", "sinst_list"]
196 c4ed32cb Iustin Pop
  src_node = args[0]
197 a5bc662a Iustin Pop
198 ec79568d Iustin Pop
  result = cl.QueryNodes(names=[src_node], fields=selected_fields,
199 77921a95 Iustin Pop
                         use_locking=False)
200 a5bc662a Iustin Pop
  src_node, sinst = result[0]
201 a5bc662a Iustin Pop
202 a5bc662a Iustin Pop
  if not sinst:
203 3a24c527 Iustin Pop
    ToStderr("No secondary instances on node %s, exiting.", src_node)
204 a5bc662a Iustin Pop
    return constants.EXIT_SUCCESS
205 a5bc662a Iustin Pop
206 c4ed32cb Iustin Pop
  if dst_node is not None:
207 77921a95 Iustin Pop
    result = cl.QueryNodes(names=[dst_node], fields=["name"],
208 77921a95 Iustin Pop
                           use_locking=False)
209 c4ed32cb Iustin Pop
    dst_node = result[0][0]
210 c4ed32cb Iustin Pop
211 c4ed32cb Iustin Pop
    if src_node == dst_node:
212 c4ed32cb Iustin Pop
      raise errors.OpPrereqError("Evacuate node needs different source and"
213 c4ed32cb Iustin Pop
                                 " target nodes (node %s given twice)" %
214 c4ed32cb Iustin Pop
                                 src_node)
215 c4ed32cb Iustin Pop
    txt_msg = "to node %s" % dst_node
216 c4ed32cb Iustin Pop
  else:
217 c4ed32cb Iustin Pop
    txt_msg = "using iallocator %s" % iallocator
218 c4ed32cb Iustin Pop
219 a5bc662a Iustin Pop
  sinst = utils.NiceSort(sinst)
220 a5bc662a Iustin Pop
221 a5bc662a Iustin Pop
  if not force and not AskUser("Relocate instance(s) %s from node\n"
222 c4ed32cb Iustin Pop
                               " %s %s?" %
223 a5bc662a Iustin Pop
                               (",".join("'%s'" % name for name in sinst),
224 c4ed32cb Iustin Pop
                               src_node, txt_msg)):
225 a5bc662a Iustin Pop
    return constants.EXIT_CONFIRMATION
226 a5bc662a Iustin Pop
227 c4ed32cb Iustin Pop
  ops = []
228 a5bc662a Iustin Pop
  for iname in sinst:
229 a5bc662a Iustin Pop
    op = opcodes.OpReplaceDisks(instance_name=iname,
230 22d31e49 Michael Hanselmann
                                remote_node=dst_node,
231 479636a3 Iustin Pop
                                mode=constants.REPLACE_DISK_CHG,
232 c4ed32cb Iustin Pop
                                iallocator=iallocator,
233 479636a3 Iustin Pop
                                disks=[])
234 c4ed32cb Iustin Pop
    ops.append(op)
235 479636a3 Iustin Pop
236 c4ed32cb Iustin Pop
  job_id = cli.SendJob(ops, cl=cl)
237 c4ed32cb Iustin Pop
  cli.PollJob(job_id, cl=cl)
238 a5bc662a Iustin Pop
239 a5bc662a Iustin Pop
240 c450e9b0 Iustin Pop
def FailoverNode(opts, args):
241 c450e9b0 Iustin Pop
  """Failover all primary instance on a node.
242 c450e9b0 Iustin Pop
243 ebf366ee Iustin Pop
  @param opts: the command line options selected by the user
244 ebf366ee Iustin Pop
  @type args: list
245 ebf366ee Iustin Pop
  @param args: should be an empty list
246 ebf366ee Iustin Pop
  @rtype: int
247 ebf366ee Iustin Pop
  @return: the desired exit code
248 ebf366ee Iustin Pop
249 c450e9b0 Iustin Pop
  """
250 479636a3 Iustin Pop
  cl = GetClient()
251 c450e9b0 Iustin Pop
  force = opts.force
252 c450e9b0 Iustin Pop
  selected_fields = ["name", "pinst_list"]
253 c450e9b0 Iustin Pop
254 2e7b8369 Iustin Pop
  # these fields are static data anyway, so it doesn't matter, but
255 2e7b8369 Iustin Pop
  # locking=True should be safer
256 2e7b8369 Iustin Pop
  result = cl.QueryNodes(names=args, fields=selected_fields,
257 77921a95 Iustin Pop
                         use_locking=False)
258 c450e9b0 Iustin Pop
  node, pinst = result[0]
259 c450e9b0 Iustin Pop
260 c450e9b0 Iustin Pop
  if not pinst:
261 3a24c527 Iustin Pop
    ToStderr("No primary instances on node %s, exiting.", node)
262 c450e9b0 Iustin Pop
    return 0
263 c450e9b0 Iustin Pop
264 c450e9b0 Iustin Pop
  pinst = utils.NiceSort(pinst)
265 c450e9b0 Iustin Pop
266 c450e9b0 Iustin Pop
  retcode = 0
267 c450e9b0 Iustin Pop
268 c450e9b0 Iustin Pop
  if not force and not AskUser("Fail over instance(s) %s?" %
269 c450e9b0 Iustin Pop
                               (",".join("'%s'" % name for name in pinst))):
270 c450e9b0 Iustin Pop
    return 2
271 c450e9b0 Iustin Pop
272 479636a3 Iustin Pop
  jex = JobExecutor(cl=cl)
273 c450e9b0 Iustin Pop
  for iname in pinst:
274 c450e9b0 Iustin Pop
    op = opcodes.OpFailoverInstance(instance_name=iname,
275 c450e9b0 Iustin Pop
                                    ignore_consistency=opts.ignore_consistency)
276 479636a3 Iustin Pop
    jex.QueueJob(iname, op)
277 479636a3 Iustin Pop
  results = jex.GetResults()
278 479636a3 Iustin Pop
  bad_cnt = len([row for row in results if not row[0]])
279 479636a3 Iustin Pop
  if bad_cnt == 0:
280 479636a3 Iustin Pop
    ToStdout("All %d instance(s) failed over successfully.", len(results))
281 c450e9b0 Iustin Pop
  else:
282 3a24c527 Iustin Pop
    ToStdout("There were errors during the failover:\n"
283 479636a3 Iustin Pop
             "%d error(s) out of %d instance(s).", bad_cnt, len(results))
284 c450e9b0 Iustin Pop
  return retcode
285 c450e9b0 Iustin Pop
286 c450e9b0 Iustin Pop
287 40ef0ed6 Iustin Pop
def MigrateNode(opts, args):
288 40ef0ed6 Iustin Pop
  """Migrate all primary instance on a node.
289 40ef0ed6 Iustin Pop
290 40ef0ed6 Iustin Pop
  """
291 40ef0ed6 Iustin Pop
  cl = GetClient()
292 40ef0ed6 Iustin Pop
  force = opts.force
293 40ef0ed6 Iustin Pop
  selected_fields = ["name", "pinst_list"]
294 40ef0ed6 Iustin Pop
295 77921a95 Iustin Pop
  result = cl.QueryNodes(names=args, fields=selected_fields, use_locking=False)
296 40ef0ed6 Iustin Pop
  node, pinst = result[0]
297 40ef0ed6 Iustin Pop
298 40ef0ed6 Iustin Pop
  if not pinst:
299 40ef0ed6 Iustin Pop
    ToStdout("No primary instances on node %s, exiting." % node)
300 40ef0ed6 Iustin Pop
    return 0
301 40ef0ed6 Iustin Pop
302 40ef0ed6 Iustin Pop
  pinst = utils.NiceSort(pinst)
303 40ef0ed6 Iustin Pop
304 40ef0ed6 Iustin Pop
  retcode = 0
305 40ef0ed6 Iustin Pop
306 40ef0ed6 Iustin Pop
  if not force and not AskUser("Migrate instance(s) %s?" %
307 40ef0ed6 Iustin Pop
                               (",".join("'%s'" % name for name in pinst))):
308 40ef0ed6 Iustin Pop
    return 2
309 40ef0ed6 Iustin Pop
310 40ef0ed6 Iustin Pop
  jex = JobExecutor(cl=cl)
311 40ef0ed6 Iustin Pop
  for iname in pinst:
312 40ef0ed6 Iustin Pop
    op = opcodes.OpMigrateInstance(instance_name=iname, live=opts.live,
313 40ef0ed6 Iustin Pop
                                   cleanup=False)
314 40ef0ed6 Iustin Pop
    jex.QueueJob(iname, op)
315 40ef0ed6 Iustin Pop
316 40ef0ed6 Iustin Pop
  results = jex.GetResults()
317 40ef0ed6 Iustin Pop
  bad_cnt = len([row for row in results if not row[0]])
318 40ef0ed6 Iustin Pop
  if bad_cnt == 0:
319 40ef0ed6 Iustin Pop
    ToStdout("All %d instance(s) migrated successfully.", len(results))
320 40ef0ed6 Iustin Pop
  else:
321 40ef0ed6 Iustin Pop
    ToStdout("There were errors during the migration:\n"
322 40ef0ed6 Iustin Pop
             "%d error(s) out of %d instance(s).", bad_cnt, len(results))
323 40ef0ed6 Iustin Pop
  return retcode
324 40ef0ed6 Iustin Pop
325 40ef0ed6 Iustin Pop
326 a8083063 Iustin Pop
def ShowNodeConfig(opts, args):
327 a8083063 Iustin Pop
  """Show node information.
328 a8083063 Iustin Pop
329 ebf366ee Iustin Pop
  @param opts: the command line options selected by the user
330 ebf366ee Iustin Pop
  @type args: list
331 ebf366ee Iustin Pop
  @param args: should either be an empty list, in which case
332 ebf366ee Iustin Pop
      we show information about all nodes, or should contain
333 ebf366ee Iustin Pop
      a list of nodes to be queried for information
334 ebf366ee Iustin Pop
  @rtype: int
335 ebf366ee Iustin Pop
  @return: the desired exit code
336 ebf366ee Iustin Pop
337 a8083063 Iustin Pop
  """
338 2e7b8369 Iustin Pop
  cl = GetClient()
339 2e7b8369 Iustin Pop
  result = cl.QueryNodes(fields=["name", "pip", "sip",
340 0b2454b9 Iustin Pop
                                 "pinst_list", "sinst_list",
341 0b2454b9 Iustin Pop
                                 "master_candidate", "drained", "offline"],
342 77921a95 Iustin Pop
                         names=args, use_locking=False)
343 a8083063 Iustin Pop
344 0b2454b9 Iustin Pop
  for (name, primary_ip, secondary_ip, pinst, sinst,
345 0b2454b9 Iustin Pop
       is_mc, drained, offline) in result:
346 3a24c527 Iustin Pop
    ToStdout("Node name: %s", name)
347 3a24c527 Iustin Pop
    ToStdout("  primary ip: %s", primary_ip)
348 3a24c527 Iustin Pop
    ToStdout("  secondary ip: %s", secondary_ip)
349 0b2454b9 Iustin Pop
    ToStdout("  master candidate: %s", is_mc)
350 0b2454b9 Iustin Pop
    ToStdout("  drained: %s", drained)
351 0b2454b9 Iustin Pop
    ToStdout("  offline: %s", offline)
352 a8083063 Iustin Pop
    if pinst:
353 3a24c527 Iustin Pop
      ToStdout("  primary for instances:")
354 ae07a1d3 Iustin Pop
      for iname in utils.NiceSort(pinst):
355 3a24c527 Iustin Pop
        ToStdout("    - %s", iname)
356 a8083063 Iustin Pop
    else:
357 3a24c527 Iustin Pop
      ToStdout("  primary for no instances")
358 a8083063 Iustin Pop
    if sinst:
359 3a24c527 Iustin Pop
      ToStdout("  secondary for instances:")
360 ae07a1d3 Iustin Pop
      for iname in utils.NiceSort(sinst):
361 3a24c527 Iustin Pop
        ToStdout("    - %s", iname)
362 a8083063 Iustin Pop
    else:
363 3a24c527 Iustin Pop
      ToStdout("  secondary for no instances")
364 a8083063 Iustin Pop
365 a8083063 Iustin Pop
  return 0
366 a8083063 Iustin Pop
367 a8083063 Iustin Pop
368 a8083063 Iustin Pop
def RemoveNode(opts, args):
369 ebf366ee Iustin Pop
  """Remove a node from the cluster.
370 ebf366ee 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 contain only one element, the name of
374 ebf366ee Iustin Pop
      the node to be removed
375 ebf366ee Iustin Pop
  @rtype: int
376 ebf366ee Iustin Pop
  @return: the desired exit code
377 ebf366ee Iustin Pop
378 ebf366ee Iustin Pop
  """
379 a8083063 Iustin Pop
  op = opcodes.OpRemoveNode(node_name=args[0])
380 a8083063 Iustin Pop
  SubmitOpCode(op)
381 ebf366ee Iustin Pop
  return 0
382 a8083063 Iustin Pop
383 a8083063 Iustin Pop
384 dcb93971 Michael Hanselmann
def ListVolumes(opts, args):
385 dcb93971 Michael Hanselmann
  """List logical volumes on node(s).
386 dcb93971 Michael Hanselmann
387 ebf366ee Iustin Pop
  @param opts: the command line options selected by the user
388 ebf366ee Iustin Pop
  @type args: list
389 ebf366ee Iustin Pop
  @param args: should either be an empty list, in which case
390 ebf366ee Iustin Pop
      we list data for all nodes, or contain a list of nodes
391 ebf366ee Iustin Pop
      to display data only for those
392 ebf366ee Iustin Pop
  @rtype: int
393 ebf366ee Iustin Pop
  @return: the desired exit code
394 ebf366ee Iustin Pop
395 dcb93971 Michael Hanselmann
  """
396 dcb93971 Michael Hanselmann
  if opts.output is None:
397 dcb93971 Michael Hanselmann
    selected_fields = ["node", "phys", "vg",
398 dcb93971 Michael Hanselmann
                       "name", "size", "instance"]
399 dcb93971 Michael Hanselmann
  else:
400 dcb93971 Michael Hanselmann
    selected_fields = opts.output.split(",")
401 dcb93971 Michael Hanselmann
402 dcb93971 Michael Hanselmann
  op = opcodes.OpQueryNodeVolumes(nodes=args, output_fields=selected_fields)
403 dcb93971 Michael Hanselmann
  output = SubmitOpCode(op)
404 dcb93971 Michael Hanselmann
405 dcb93971 Michael Hanselmann
  if not opts.no_headers:
406 137161c9 Michael Hanselmann
    headers = {"node": "Node", "phys": "PhysDev",
407 137161c9 Michael Hanselmann
               "vg": "VG", "name": "Name",
408 137161c9 Michael Hanselmann
               "size": "Size", "instance": "Instance"}
409 137161c9 Michael Hanselmann
  else:
410 137161c9 Michael Hanselmann
    headers = None
411 137161c9 Michael Hanselmann
412 9fbfbb7b Iustin Pop
  unitfields = ["size"]
413 137161c9 Michael Hanselmann
414 137161c9 Michael Hanselmann
  numfields = ["size"]
415 137161c9 Michael Hanselmann
416 16be8703 Iustin Pop
  data = GenerateTable(separator=opts.separator, headers=headers,
417 16be8703 Iustin Pop
                       fields=selected_fields, unitfields=unitfields,
418 9fbfbb7b Iustin Pop
                       numfields=numfields, data=output, units=opts.units)
419 16be8703 Iustin Pop
420 16be8703 Iustin Pop
  for line in data:
421 3a24c527 Iustin Pop
    ToStdout(line)
422 dcb93971 Michael Hanselmann
423 dcb93971 Michael Hanselmann
  return 0
424 dcb93971 Michael Hanselmann
425 dcb93971 Michael Hanselmann
426 b31c8676 Iustin Pop
def SetNodeParams(opts, args):
427 b31c8676 Iustin Pop
  """Modifies a node.
428 b31c8676 Iustin Pop
429 b31c8676 Iustin Pop
  @param opts: the command line options selected by the user
430 b31c8676 Iustin Pop
  @type args: list
431 b31c8676 Iustin Pop
  @param args: should contain only one element, the node name
432 b31c8676 Iustin Pop
  @rtype: int
433 b31c8676 Iustin Pop
  @return: the desired exit code
434 b31c8676 Iustin Pop
435 b31c8676 Iustin Pop
  """
436 c9d443ea Iustin Pop
  if [opts.master_candidate, opts.drained, opts.offline].count(None) == 3:
437 b31c8676 Iustin Pop
    ToStderr("Please give at least one of the parameters.")
438 b31c8676 Iustin Pop
    return 1
439 b31c8676 Iustin Pop
440 3a5ba66a Iustin Pop
  if opts.master_candidate is not None:
441 3a5ba66a Iustin Pop
    candidate = opts.master_candidate == 'yes'
442 3a5ba66a Iustin Pop
  else:
443 3a5ba66a Iustin Pop
    candidate = None
444 3a5ba66a Iustin Pop
  if opts.offline is not None:
445 3a5ba66a Iustin Pop
    offline = opts.offline == 'yes'
446 3a5ba66a Iustin Pop
  else:
447 3a5ba66a Iustin Pop
    offline = None
448 c9d443ea Iustin Pop
449 c9d443ea Iustin Pop
  if opts.drained is not None:
450 c9d443ea Iustin Pop
    drained = opts.drained == 'yes'
451 c9d443ea Iustin Pop
  else:
452 c9d443ea Iustin Pop
    drained = None
453 b31c8676 Iustin Pop
  op = opcodes.OpSetNodeParams(node_name=args[0],
454 3a5ba66a Iustin Pop
                               master_candidate=candidate,
455 3a5ba66a Iustin Pop
                               offline=offline,
456 c9d443ea Iustin Pop
                               drained=drained,
457 3a5ba66a Iustin Pop
                               force=opts.force)
458 b31c8676 Iustin Pop
459 b31c8676 Iustin Pop
  # even if here we process the result, we allow submit only
460 b31c8676 Iustin Pop
  result = SubmitOrSend(op, opts)
461 b31c8676 Iustin Pop
462 b31c8676 Iustin Pop
  if result:
463 b31c8676 Iustin Pop
    ToStdout("Modified node %s", args[0])
464 b31c8676 Iustin Pop
    for param, data in result:
465 b31c8676 Iustin Pop
      ToStdout(" - %-5s -> %s", param, data)
466 b31c8676 Iustin Pop
  return 0
467 b31c8676 Iustin Pop
468 b31c8676 Iustin Pop
469 a8083063 Iustin Pop
commands = {
470 a8083063 Iustin Pop
  'add': (AddNode, ARGS_ONE,
471 a8083063 Iustin Pop
          [DEBUG_OPT,
472 a8083063 Iustin Pop
           make_option("-s", "--secondary-ip", dest="secondary_ip",
473 a8083063 Iustin Pop
                       help="Specify the secondary ip for the node",
474 e7c6e02b Michael Hanselmann
                       metavar="ADDRESS", default=None),
475 e7c6e02b Michael Hanselmann
           make_option("--readd", dest="readd",
476 e7c6e02b Michael Hanselmann
                       default=False, action="store_true",
477 e7c6e02b Michael Hanselmann
                       help="Readd old node after replacing it"),
478 c4b6c29c Michael Hanselmann
           make_option("--no-ssh-key-check", dest="ssh_key_check",
479 c4b6c29c Michael Hanselmann
                       default=True, action="store_false",
480 c4b6c29c Michael Hanselmann
                       help="Disable SSH key fingerprint checking"),
481 e7c6e02b Michael Hanselmann
           ],
482 c4b6c29c Michael Hanselmann
          "[-s ip] [--readd] [--no-ssh-key-check] <node_name>",
483 c4b6c29c Michael Hanselmann
          "Add a node to the cluster"),
484 c4ed32cb Iustin Pop
  'evacuate': (EvacuateNode, ARGS_ONE,
485 c4ed32cb Iustin Pop
               [DEBUG_OPT, FORCE_OPT,
486 c4ed32cb Iustin Pop
                make_option("-n", "--new-secondary", dest="dst_node",
487 c4ed32cb Iustin Pop
                            help="New secondary node", metavar="NODE",
488 c4ed32cb Iustin Pop
                            default=None),
489 633b36db Iustin Pop
                make_option("-I", "--iallocator", metavar="<NAME>",
490 c4ed32cb Iustin Pop
                            help="Select new secondary for the instance"
491 c4ed32cb Iustin Pop
                            " automatically using the"
492 c4ed32cb Iustin Pop
                            " <NAME> iallocator plugin",
493 c4ed32cb Iustin Pop
                            default=None, type="string"),
494 c4ed32cb Iustin Pop
                ],
495 633b36db Iustin Pop
               "[-f] {-I <iallocator> | -n <dst>} <node>",
496 c4ed32cb Iustin Pop
               "Relocate the secondary instances from a node"
497 c4ed32cb Iustin Pop
               " to other nodes (only for instances with drbd disk template)"),
498 c450e9b0 Iustin Pop
  'failover': (FailoverNode, ARGS_ONE,
499 c450e9b0 Iustin Pop
               [DEBUG_OPT, FORCE_OPT,
500 c450e9b0 Iustin Pop
                make_option("--ignore-consistency", dest="ignore_consistency",
501 c450e9b0 Iustin Pop
                            action="store_true", default=False,
502 c450e9b0 Iustin Pop
                            help="Ignore the consistency of the disks on"
503 c450e9b0 Iustin Pop
                            " the secondary"),
504 c450e9b0 Iustin Pop
                ],
505 9a033156 Iustin Pop
               "[-f] <node>",
506 c450e9b0 Iustin Pop
               "Stops the primary instances on a node and start them on their"
507 abdf0113 Iustin Pop
               " secondary node (only for instances with drbd disk template)"),
508 40ef0ed6 Iustin Pop
  'migrate': (MigrateNode, ARGS_ONE,
509 40ef0ed6 Iustin Pop
               [DEBUG_OPT, FORCE_OPT,
510 40ef0ed6 Iustin Pop
                make_option("--non-live", dest="live",
511 40ef0ed6 Iustin Pop
                            default=True, action="store_false",
512 40ef0ed6 Iustin Pop
                            help="Do a non-live migration (this usually means"
513 40ef0ed6 Iustin Pop
                            " freeze the instance, save the state,"
514 40ef0ed6 Iustin Pop
                            " transfer and only then resume running on the"
515 40ef0ed6 Iustin Pop
                            " secondary node)"),
516 40ef0ed6 Iustin Pop
                ],
517 40ef0ed6 Iustin Pop
               "[-f] <node>",
518 40ef0ed6 Iustin Pop
               "Migrate all the primary instance on a node away from it"
519 40ef0ed6 Iustin Pop
               " (only for instances of type drbd)"),
520 a8083063 Iustin Pop
  'info': (ShowNodeConfig, ARGS_ANY, [DEBUG_OPT],
521 9a033156 Iustin Pop
           "[<node_name>...]", "Show information about the node(s)"),
522 f1de3563 Iustin Pop
  'list': (ListNodes, ARGS_ANY,
523 f1de3563 Iustin Pop
           [DEBUG_OPT, NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT, SYNC_OPT],
524 f1de3563 Iustin Pop
           "[nodes...]",
525 f1de3563 Iustin Pop
           "Lists the nodes in the cluster. The available fields"
526 cc3bcec8 Guido Trotter
           " are (see the man page for details): %s"
527 cc3bcec8 Guido Trotter
           " The default field list is (in order): %s." %
528 cc3bcec8 Guido Trotter
           (", ".join(_LIST_HEADERS), ", ".join(_LIST_DEF_FIELDS))),
529 b31c8676 Iustin Pop
  'modify': (SetNodeParams, ARGS_ONE,
530 b31c8676 Iustin Pop
             [DEBUG_OPT, FORCE_OPT,
531 b31c8676 Iustin Pop
              SUBMIT_OPT,
532 b31c8676 Iustin Pop
              make_option("-C", "--master-candidate", dest="master_candidate",
533 b31c8676 Iustin Pop
                          choices=('yes', 'no'), default=None,
534 949bdabe Iustin Pop
                          metavar="yes|no",
535 b31c8676 Iustin Pop
                          help="Set the master_candidate flag on the node"),
536 949bdabe Iustin Pop
537 949bdabe Iustin Pop
              make_option("-O", "--offline", dest="offline", metavar="yes|no",
538 3a5ba66a Iustin Pop
                          choices=('yes', 'no'), default=None,
539 3a5ba66a Iustin Pop
                          help="Set the offline flag on the node"),
540 949bdabe Iustin Pop
              make_option("-D", "--drained", dest="drained", metavar="yes|no",
541 c9d443ea Iustin Pop
                          choices=('yes', 'no'), default=None,
542 c9d443ea Iustin Pop
                          help="Set the drained flag on the node"),
543 b31c8676 Iustin Pop
              ],
544 b31c8676 Iustin Pop
             "<instance>", "Alters the parameters of an instance"),
545 a8083063 Iustin Pop
  'remove': (RemoveNode, ARGS_ONE, [DEBUG_OPT],
546 9a033156 Iustin Pop
             "<node_name>", "Removes a node from the cluster"),
547 dcb93971 Michael Hanselmann
  'volumes': (ListVolumes, ARGS_ANY,
548 dcb93971 Michael Hanselmann
              [DEBUG_OPT, NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT],
549 9a033156 Iustin Pop
              "[<node_name>...]", "List logical volumes on node(s)"),
550 846baef9 Iustin Pop
  'list-tags': (ListTags, ARGS_ONE, [DEBUG_OPT],
551 9a033156 Iustin Pop
                "<node_name>", "List the tags of the given node"),
552 810c50b7 Iustin Pop
  'add-tags': (AddTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
553 9a033156 Iustin Pop
               "<node_name> tag...", "Add tags to the given node"),
554 810c50b7 Iustin Pop
  'remove-tags': (RemoveTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
555 9a033156 Iustin Pop
                  "<node_name> tag...", "Remove tags from the given node"),
556 a8083063 Iustin Pop
  }
557 a8083063 Iustin Pop
558 a8083063 Iustin Pop
559 a8083063 Iustin Pop
if __name__ == '__main__':
560 846baef9 Iustin Pop
  sys.exit(GenericMain(commands, override={"tag_type": constants.TAG_NODE}))