gnt-backup 8.9 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/usr/bin/python
#

# Copyright (C) 2006, 2007 Google Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.


22
23
24
25
# pylint: disable-msg=W0401,W0614
# W0401: Wildcard import ganeti.cli
# W0614: Unused import %s from wildcard import (since we need cli)

26
27
28
29
30
31
import sys
from optparse import make_option

from ganeti.cli import *
from ganeti import opcodes
from ganeti import constants
32
33
from ganeti import errors
from ganeti import utils
34

35

36
37
_VALUE_TRUE = "true"

38
39
40
def PrintExportList(opts, args):
  """Prints a list of all the exported system images.

41
42
43
44
45
  @param opts: the command line options selected by the user
  @type args: list
  @param args: should be an empty list
  @rtype: int
  @return: the desired exit code
46
47

  """
48
  exports = GetClient().QueryExports(opts.nodes, True)
49
  retcode = 0
50
  for node in exports:
51
52
    ToStdout("Node: %s", node)
    ToStdout("Exports:")
53
54
    if isinstance(exports[node], list):
      for instance_name in exports[node]:
55
        ToStdout("\t%s", instance_name)
56
    else:
57
      ToStdout("  Could not get exports list")
58
59
      retcode = 1
  return retcode
60
61
62
63
64


def ExportInstance(opts, args):
  """Export an instance to an image in the cluster.

65
66
67
68
69
70
  @param opts: the command line options selected by the user
  @type args: list
  @param args: should contain only one element, the name
      of the instance to be exported
  @rtype: int
  @return: the desired exit code
71
72
73
74
75
76
77
78

  """
  op = opcodes.OpExportInstance(instance_name=args[0],
                                target_node=opts.node,
                                shutdown=opts.shutdown)

  SubmitOpCode(op)

79

80
81
82
def ImportInstance(opts, args):
  """Add an instance to the cluster.

83
84
85
86
87
  @param opts: the command line options selected by the user
  @type args: list
  @param args: should contain only one element, the new instance name
  @rtype: int
  @return: the desired exit code
88
89
90
91

  """
  instance = args[0]

92
93
  (pnode, snode) = SplitNodeOption(opts.node)

94
95
96
97
98
  hypervisor = None
  hvparams = {}
  if opts.hypervisor:
    hypervisor, hvparams = opts.hypervisor

99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
  if opts.nics:
    try:
      nic_max = max(int(nidx[0])+1 for nidx in opts.nics)
    except ValueError, err:
      raise errors.OpPrereqError("Invalid NIC index passed: %s" % str(err))
    nics = [{}] * nic_max
    for nidx, ndict in opts.nics.items():
      nidx = int(nidx)
      nics[nidx] = ndict
  elif opts.no_nics:
    # no nics
    nics = []
  else:
    # default of one nic, all auto
    nics = [{}]

  if opts.disk_template == constants.DT_DISKLESS:
    if opts.disks:
      raise errors.OpPrereqError("Diskless instance but disk"
                                 " information passed")
    disks = []
  else:
    if not opts.disks:
      raise errors.OpPrereqError("No disk information specified")
    try:
      disk_max = max(int(didx[0])+1 for didx in opts.disks)
    except ValueError, err:
      raise errors.OpPrereqError("Invalid disk index passed: %s" % str(err))
    disks = [{}] * disk_max
    for didx, ddict in opts.disks:
      didx = int(didx)
      if "size" not in ddict:
        raise errors.OpPrereqError("Missing size for disk %d" % didx)
      try:
        ddict["size"] = utils.ParseUnit(ddict["size"])
      except ValueError, err:
        raise errors.OpPrereqError("Invalid disk size for disk %d: %s" %
                                   (didx, err))
      disks[didx] = ddict

139
140
  utils.ForceDictType(opts.beparams, constants.BES_PARAMETER_TYPES)
  utils.ForceDictType(opts.hvparams, constants.HVS_PARAMETER_TYPES)
141

Iustin Pop's avatar
Iustin Pop committed
142
  op = opcodes.OpCreateInstance(instance_name=instance,
143
                                disk_template=opts.disk_template,
144
145
                                disks=disks,
                                nics=nics,
146
                                mode=constants.INSTANCE_IMPORT,
147
                                pnode=pnode, snode=snode,
Iustin Pop's avatar
Iustin Pop committed
148
                                ip_check=opts.ip_check,
149
                                start=False,
150
                                src_node=opts.src_node, src_path=opts.src_dir,
151
                                wait_for_sync=opts.wait_for_sync,
152
153
                                file_storage_dir=opts.file_storage_dir,
                                file_driver=opts.file_driver,
154
                                iallocator=opts.iallocator,
155
156
157
                                hypervisor=hypervisor,
                                hvparams=hvparams,
                                beparams=opts.beparams)
158

159
160
161
162
  SubmitOpCode(op)
  return 0


163
164
165
def RemoveExport(opts, args):
  """Remove an export from the cluster.

166
167
168
169
170
171
  @param opts: the command line options selected by the user
  @type args: list
  @param args: should contain only one element, the name of the
      instance whose backup should be removed
  @rtype: int
  @return: the desired exit code
172
173
174
175
176
177
178
179
180

  """
  instance = args[0]
  op = opcodes.OpRemoveExport(instance_name=args[0])

  SubmitOpCode(op)
  return 0


181
182
183
# this is defined separately due to readability only
import_opts = [
  DEBUG_OPT,
184
185
186
  make_option("-n", "--node", dest="node",
              help="Target node and optional secondary node",
              metavar="<pnode>[:<snode>]"),
187
188
189
  keyval_option("-B", "--backend", dest="beparams",
                type="keyval", default={},
                help="Backend parameters"),
190
  make_option("-t", "--disk-template", dest="disk_template",
Iustin Pop's avatar
Iustin Pop committed
191
              help="Custom disk setup (diskless, file, plain, drbd)",
192
              default=None, metavar="TEMPL"),
193
194
195
196
197
198
199
200
201
202
  ikv_option("--disk", help="Disk information",
             default=[], dest="disks",
             action="append",
             type="identkeyval"),
  ikv_option("--net", help="NIC information",
             default=[], dest="nics",
             action="append",
             type="identkeyval"),
  make_option("--no-nics", default=False, action="store_true",
              help="Do not create any network cards for the instance"),
203
204
205
206
207
208
  make_option("--no-wait-for-sync", dest="wait_for_sync", default=True,
              action="store_false", help="Don't wait for sync (DANGEROUS!)"),
  make_option("--src-node", dest="src_node", help="Source node",
              metavar="<node>"),
  make_option("--src-dir", dest="src_dir", help="Source directory",
              metavar="<dir>"),
209
210
211
  make_option("--no-ip-check", dest="ip_check", default=True,
              action="store_false", help="Don't check that the instance's IP"
              " is alive"),
212
  make_option("-I", "--iallocator", metavar="<NAME>",
213
214
              help="Select nodes for the instance automatically using the"
              " <NAME> iallocator plugin", default=None, type="string"),
215
216
217
218
219
220
  make_option("--file-storage-dir", dest="file_storage_dir",
              help="Relative path under default cluster-wide file storage dir"
              " to store file-based disks", default=None,
              metavar="<DIR>"),
  make_option("--file-driver", dest="file_driver", help="Driver to use"
              " for image files", default="loop", metavar="<DRIVER>"),
221
222
223
224
  ikv_option("-H", "--hypervisor", dest="hypervisor",
              help="Hypervisor and hypervisor options, in the format"
              " hypervisor:option=value,option=value,...", default=None,
              type="identkeyval"),
225
226
227
228
229
  ]

commands = {
  'list': (PrintExportList, ARGS_NONE,
           [DEBUG_OPT,
230
            make_option("--node", dest="nodes", default=[], action="append",
Guido Trotter's avatar
Guido Trotter committed
231
232
                        help="List only backups stored on this node"
                             " (can be used multiple times)"),
233
            ],
234
           "", "Lists instance exports available in the ganeti cluster"),
235
  'export': (ExportInstance, ARGS_ONE,
236
237
238
             [DEBUG_OPT, FORCE_OPT,
              make_option("-n", "--node", dest="node", help="Target node",
                          metavar="<node>"),
239
240
241
              make_option("","--noshutdown", dest="shutdown",
                          action="store_false", default=True,
                          help="Don't shutdown the instance (unsafe)"), ],
242
             "-n <target_node> [opts...] <name>",
243
             "Exports an instance to an image"),
244
245
246
  'import': (ImportInstance, ARGS_ONE, import_opts,
             ("[...] -t disk-type -n node[:secondary-node]"
              " <name>"),
247
             "Imports an instance from an exported image"),
248
249
  'remove': (RemoveExport, ARGS_ONE,
             [DEBUG_OPT],
250
             "<name>",
251
             "Remove exports of named instance from the filesystem."),
252
253
254
  }

if __name__ == '__main__':
255
  sys.exit(GenericMain(commands))