gnt-backup 8.8 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
  ValidateBeParams(opts.beparams)

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

158
159
160
161
  SubmitOpCode(op)
  return 0


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

165
166
167
168
169
170
  @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
171
172
173
174
175
176
177
178
179

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

  SubmitOpCode(op)
  return 0


180
181
182
# this is defined separately due to readability only
import_opts = [
  DEBUG_OPT,
183
184
185
  make_option("-n", "--node", dest="node",
              help="Target node and optional secondary node",
              metavar="<pnode>[:<snode>]"),
186
187
188
  keyval_option("-B", "--backend", dest="beparams",
                type="keyval", default={},
                help="Backend parameters"),
189
  make_option("-t", "--disk-template", dest="disk_template",
Iustin Pop's avatar
Iustin Pop committed
190
              help="Custom disk setup (diskless, file, plain, drbd)",
191
              default=None, metavar="TEMPL"),
192
193
194
195
196
197
198
199
200
201
  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"),
202
203
204
205
206
207
  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>"),
208
209
210
  make_option("--no-ip-check", dest="ip_check", default=True,
              action="store_false", help="Don't check that the instance's IP"
              " is alive"),
211
  make_option("-I", "--iallocator", metavar="<NAME>",
212
213
              help="Select nodes for the instance automatically using the"
              " <NAME> iallocator plugin", default=None, type="string"),
214
215
216
217
218
219
  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>"),
220
221
222
223
  ikv_option("-H", "--hypervisor", dest="hypervisor",
              help="Hypervisor and hypervisor options, in the format"
              " hypervisor:option=value,option=value,...", default=None,
              type="identkeyval"),
224
225
226
227
228
  ]

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

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