Revision 47f8a2d2

b/daemons/ganeti-noded
353 353
  # export/import  --------------------------
354 354

  
355 355
  @staticmethod
356
  def perspective_snapshot_export(params):
357
    """Export a given snapshot.
358

  
359
    """
360
    disk = objects.Disk.FromDict(params[0])
361
    dest_node = params[1]
362
    instance = objects.Instance.FromDict(params[2])
363
    cluster_name = params[3]
364
    dev_idx = params[4]
365
    debug = params[5]
366
    return backend.ExportSnapshot(disk, dest_node, instance,
367
                                  cluster_name, dev_idx, debug)
368

  
369
  @staticmethod
370 356
  def perspective_finalize_export(params):
371 357
    """Expose the finalize export functionality.
372 358

  
......
488 474
    return backend.RunRenameInstance(inst, old_name, debug)
489 475

  
490 476
  @staticmethod
491
  def perspective_instance_os_import(params):
492
    """Run the import function of an OS onto a given instance.
493

  
494
    """
495
    inst_s, src_node, src_images, cluster_name, debug = params
496
    inst = objects.Instance.FromDict(inst_s)
497
    return backend.ImportOSIntoInstance(inst, src_node, src_images,
498
                                        cluster_name, debug)
499

  
500
  @staticmethod
501 477
  def perspective_instance_shutdown(params):
502 478
    """Shutdown an instance.
503 479

  
b/lib/backend.py
2016 2016
          disk.unique_id, disk.dev_type)
2017 2017

  
2018 2018

  
2019
def ExportSnapshot(disk, dest_node, instance, cluster_name, idx, debug):
2020
  """Export a block device snapshot to a remote node.
2021

  
2022
  @type disk: L{objects.Disk}
2023
  @param disk: the description of the disk to export
2024
  @type dest_node: str
2025
  @param dest_node: the destination node to export to
2026
  @type instance: L{objects.Instance}
2027
  @param instance: the instance object to whom the disk belongs
2028
  @type cluster_name: str
2029
  @param cluster_name: the cluster name, needed for SSH hostalias
2030
  @type idx: int
2031
  @param idx: the index of the disk in the instance's disk list,
2032
      used to export to the OS scripts environment
2033
  @type debug: integer
2034
  @param debug: debug level, passed to the OS scripts
2035
  @rtype: None
2036

  
2037
  """
2038
  inst_os = OSFromDisk(instance.os)
2039
  export_env = OSEnvironment(instance, inst_os, debug)
2040

  
2041
  export_script = inst_os.export_script
2042

  
2043
  logfile = _InstanceLogName("export", inst_os.name, instance.name)
2044
  if not os.path.exists(constants.LOG_OS_DIR):
2045
    os.mkdir(constants.LOG_OS_DIR, 0750)
2046

  
2047
  real_disk = _OpenRealBD(disk)
2048

  
2049
  export_env['EXPORT_DEVICE'] = real_disk.dev_path
2050
  export_env['EXPORT_INDEX'] = str(idx)
2051

  
2052
  destdir = utils.PathJoin(constants.EXPORT_DIR, instance.name + ".new")
2053
  destfile = disk.physical_id[1]
2054

  
2055
  # the target command is built out of three individual commands,
2056
  # which are joined by pipes; we check each individual command for
2057
  # valid parameters
2058
  expcmd = utils.BuildShellCmd("set -e; set -o pipefail; cd %s; %s 2>%s",
2059
                               inst_os.path, export_script, logfile)
2060

  
2061
  comprcmd = "gzip"
2062

  
2063
  destcmd = utils.BuildShellCmd("mkdir -p %s && cat > %s",
2064
                                destdir, utils.PathJoin(destdir, destfile))
2065
  remotecmd = _GetSshRunner(cluster_name).BuildCmd(dest_node,
2066
                                                   constants.GANETI_RUNAS,
2067
                                                   destcmd)
2068

  
2069
  # all commands have been checked, so we're safe to combine them
2070
  command = '|'.join([expcmd, comprcmd, utils.ShellQuoteArgs(remotecmd)])
2071

  
2072
  result = utils.RunCmd(["bash", "-c", command], env=export_env)
2073

  
2074
  if result.failed:
2075
    _Fail("OS snapshot export command '%s' returned error: %s"
2076
          " output: %s", command, result.fail_reason, result.output)
2077

  
2078

  
2079 2019
def FinalizeExport(instance, snap_disks):
2080 2020
  """Write out the export configuration information.
2081 2021

  
......
2175 2115
  return config.Dumps()
2176 2116

  
2177 2117

  
2178
def ImportOSIntoInstance(instance, src_node, src_images, cluster_name, debug):
2179
  """Import an os image into an instance.
2180

  
2181
  @type instance: L{objects.Instance}
2182
  @param instance: instance to import the disks into
2183
  @type src_node: string
2184
  @param src_node: source node for the disk images
2185
  @type src_images: list of string
2186
  @param src_images: absolute paths of the disk images
2187
  @type debug: integer
2188
  @param debug: debug level, passed to the OS scripts
2189
  @rtype: list of boolean
2190
  @return: each boolean represent the success of importing the n-th disk
2191

  
2192
  """
2193
  inst_os = OSFromDisk(instance.os)
2194
  import_env = OSEnvironment(instance, inst_os, debug)
2195
  import_script = inst_os.import_script
2196

  
2197
  logfile = _InstanceLogName("import", instance.os, instance.name)
2198
  if not os.path.exists(constants.LOG_OS_DIR):
2199
    os.mkdir(constants.LOG_OS_DIR, 0750)
2200

  
2201
  comprcmd = "gunzip"
2202
  impcmd = utils.BuildShellCmd("(cd %s; %s >%s 2>&1)", inst_os.path,
2203
                               import_script, logfile)
2204

  
2205
  final_result = []
2206
  for idx, image in enumerate(src_images):
2207
    if image:
2208
      destcmd = utils.BuildShellCmd('cat %s', image)
2209
      remotecmd = _GetSshRunner(cluster_name).BuildCmd(src_node,
2210
                                                       constants.GANETI_RUNAS,
2211
                                                       destcmd)
2212
      command = '|'.join([utils.ShellQuoteArgs(remotecmd), comprcmd, impcmd])
2213
      import_env['IMPORT_DEVICE'] = import_env['DISK_%d_PATH' % idx]
2214
      import_env['IMPORT_INDEX'] = str(idx)
2215
      result = utils.RunCmd(command, env=import_env)
2216
      if result.failed:
2217
        logging.error("Disk import command '%s' returned error: %s"
2218
                      " output: %s", command, result.fail_reason,
2219
                      result.output)
2220
        final_result.append("error importing disk %d: %s, %s" %
2221
                            (idx, result.fail_reason, result.output[-100]))
2222

  
2223
  if final_result:
2224
    _Fail("; ".join(final_result), log=False)
2225

  
2226

  
2227 2118
def ListExports():
2228 2119
  """Return a list of exports currently available on this machine.
2229 2120

  
b/lib/rpc.py
1002 1002
    """
1003 1003
    return self._SingleNodeCall(node, "blockdev_snapshot", [cf_bdev.ToDict()])
1004 1004

  
1005
  def call_snapshot_export(self, node, snap_bdev, dest_node, instance,
1006
                           cluster_name, idx, debug):
1007
    """Request the export of a given snapshot.
1008

  
1009
    This is a single-node call.
1010

  
1011
    """
1012
    return self._SingleNodeCall(node, "snapshot_export",
1013
                                [snap_bdev.ToDict(), dest_node,
1014
                                 self._InstDict(instance), cluster_name,
1015
                                 idx, debug])
1016

  
1017 1005
  def call_finalize_export(self, node, instance, snap_disks):
1018 1006
    """Request the completion of an export operation.
1019 1007

  
......
1040 1028
    """
1041 1029
    return self._SingleNodeCall(node, "export_info", [path])
1042 1030

  
1043
  def call_instance_os_import(self, node, inst, src_node, src_images,
1044
                              cluster_name, debug):
1045
    """Request the import of a backup into an instance.
1046

  
1047
    This is a single-node call.
1048

  
1049
    """
1050
    return self._SingleNodeCall(node, "instance_os_import",
1051
                                [self._InstDict(inst), src_node, src_images,
1052
                                 cluster_name, debug])
1053

  
1054 1031
  def call_export_list(self, node_list):
1055 1032
    """Gets the stored exports list.
1056 1033

  

Also available in: Unified diff