Revision e311ed53

b/lib/cmdlib.py
45 45
from ganeti import serializer
46 46
from ganeti import ssconf
47 47
from ganeti import uidpool
48
from ganeti import compat
48 49

  
49 50

  
50 51
class LogicalUnit(object):
......
8871 8872
        raise errors.OpPrereqError("Export not supported for instances with"
8872 8873
                                   " file-based disks", errors.ECODE_INVAL)
8873 8874

  
8875
  def _CreateSnapshots(self, feedback_fn):
8876
    """Creates an LVM snapshot for every disk of the instance.
8877

  
8878
    @return: List of snapshots as L{objects.Disk} instances
8879

  
8880
    """
8881
    instance = self.instance
8882
    src_node = instance.primary_node
8883

  
8884
    vgname = self.cfg.GetVGName()
8885

  
8886
    snap_disks = []
8887

  
8888
    for idx, disk in enumerate(instance.disks):
8889
      feedback_fn("Creating a snapshot of disk/%s on node %s" %
8890
                  (idx, src_node))
8891

  
8892
      # result.payload will be a snapshot of an lvm leaf of the one we
8893
      # passed
8894
      result = self.rpc.call_blockdev_snapshot(src_node, disk)
8895
      msg = result.fail_msg
8896
      if msg:
8897
        self.LogWarning("Could not snapshot disk/%s on node %s: %s",
8898
                        idx, src_node, msg)
8899
        snap_disks.append(False)
8900
      else:
8901
        disk_id = (vgname, result.payload)
8902
        new_dev = objects.Disk(dev_type=constants.LD_LV, size=disk.size,
8903
                               logical_id=disk_id, physical_id=disk_id,
8904
                               iv_name=disk.iv_name)
8905
        snap_disks.append(new_dev)
8906

  
8907
    return snap_disks
8908

  
8909
  def _RemoveSnapshot(self, feedback_fn, snap_disks, disk_index):
8910
    """Removes an LVM snapshot.
8911

  
8912
    @type snap_disks: list
8913
    @param snap_disks: The list of all snapshots as returned by
8914
                       L{_CreateSnapshots}
8915
    @type disk_index: number
8916
    @param disk_index: Index of the snapshot to be removed
8917
    @rtype: bool
8918
    @return: Whether removal was successful or not
8919

  
8920
    """
8921
    disk = snap_disks[disk_index]
8922
    if disk:
8923
      src_node = self.instance.primary_node
8924

  
8925
      feedback_fn("Removing snapshot of disk/%s on node %s" %
8926
                  (disk_index, src_node))
8927

  
8928
      result = self.rpc.call_blockdev_remove(src_node, disk)
8929
      if not result.fail_msg:
8930
        return True
8931

  
8932
      self.LogWarning("Could not remove snapshot for disk/%d from node"
8933
                      " %s: %s", disk_index, src_node, result.fail_msg)
8934

  
8935
    return False
8936

  
8937
  def _CleanupExports(self, feedback_fn):
8938
    """Removes exports of current instance from all other nodes.
8939

  
8940
    If an instance in a cluster with nodes A..D was exported to node C, its
8941
    exports will be removed from the nodes A, B and D.
8942

  
8943
    """
8944
    nodelist = self.cfg.GetNodeList()
8945
    nodelist.remove(self.dst_node.name)
8946

  
8947
    # on one-node clusters nodelist will be empty after the removal
8948
    # if we proceed the backup would be removed because OpQueryExports
8949
    # substitutes an empty list with the full cluster node list.
8950
    iname = self.instance.name
8951
    if nodelist:
8952
      feedback_fn("Removing old exports for instance %s" % iname)
8953
      exportlist = self.rpc.call_export_list(nodelist)
8954
      for node in exportlist:
8955
        if exportlist[node].fail_msg:
8956
          continue
8957
        if iname in exportlist[node].payload:
8958
          msg = self.rpc.call_export_remove(node, iname).fail_msg
8959
          if msg:
8960
            self.LogWarning("Could not remove older export for instance %s"
8961
                            " on node %s: %s", iname, node, msg)
8962

  
8874 8963
  def Exec(self, feedback_fn):
8875 8964
    """Export an instance to an image in the cluster.
8876 8965

  
......
8887 8976
      result.Raise("Could not shutdown instance %s on"
8888 8977
                   " node %s" % (instance.name, src_node))
8889 8978

  
8890
    vgname = self.cfg.GetVGName()
8891

  
8892
    snap_disks = []
8893

  
8894 8979
    # set the disks ID correctly since call_instance_start needs the
8895 8980
    # correct drbd minor to create the symlinks
8896 8981
    for disk in instance.disks:
......
8906 8991
    try:
8907 8992
      # per-disk results
8908 8993
      dresults = []
8994
      removed_snaps = [False] * len(instance.disks)
8995

  
8996
      snap_disks = None
8909 8997
      try:
8910
        for idx, disk in enumerate(instance.disks):
8911
          feedback_fn("Creating a snapshot of disk/%s on node %s" %
8912
                      (idx, src_node))
8913

  
8914
          # result.payload will be a snapshot of an lvm leaf of the one we
8915
          # passed
8916
          result = self.rpc.call_blockdev_snapshot(src_node, disk)
8917
          msg = result.fail_msg
8918
          if msg:
8919
            self.LogWarning("Could not snapshot disk/%s on node %s: %s",
8920
                            idx, src_node, msg)
8921
            snap_disks.append(False)
8922
          else:
8923
            disk_id = (vgname, result.payload)
8924
            new_dev = objects.Disk(dev_type=constants.LD_LV, size=disk.size,
8925
                                   logical_id=disk_id, physical_id=disk_id,
8926
                                   iv_name=disk.iv_name)
8927
            snap_disks.append(new_dev)
8998
        try:
8999
          snap_disks = self._CreateSnapshots(feedback_fn)
9000
        finally:
9001
          if self.op.shutdown and instance.admin_up:
9002
            feedback_fn("Starting instance %s" % instance.name)
9003
            result = self.rpc.call_instance_start(src_node, instance,
9004
                                                  None, None)
9005
            msg = result.fail_msg
9006
            if msg:
9007
              _ShutdownInstanceDisks(self, instance)
9008
              raise errors.OpExecError("Could not start instance: %s" % msg)
8928 9009

  
8929
      finally:
8930
        if self.op.shutdown and instance.admin_up:
8931
          feedback_fn("Starting instance %s" % instance.name)
8932
          result = self.rpc.call_instance_start(src_node, instance, None, None)
8933
          msg = result.fail_msg
8934
          if msg:
8935
            _ShutdownInstanceDisks(self, instance)
8936
            raise errors.OpExecError("Could not start instance: %s" % msg)
8937

  
8938
      # TODO: check for size
8939

  
8940
      cluster_name = self.cfg.GetClusterName()
8941
      for idx, dev in enumerate(snap_disks):
8942
        feedback_fn("Exporting snapshot %s from %s to %s" %
8943
                    (idx, src_node, dst_node.name))
8944
        if dev:
8945
          # FIXME: pass debug from opcode to backend
8946
          result = self.rpc.call_snapshot_export(src_node, dev, dst_node.name,
8947
                                                 instance, cluster_name,
8948
                                                 idx, self.op.debug_level)
8949
          msg = result.fail_msg
8950
          if msg:
8951
            self.LogWarning("Could not export disk/%s from node %s to"
8952
                            " node %s: %s", idx, src_node, dst_node.name, msg)
8953
            dresults.append(False)
9010
        assert len(snap_disks) == len(instance.disks)
9011
        assert len(removed_snaps) == len(instance.disks)
9012

  
9013
        # TODO: check for size
9014

  
9015
        cluster_name = self.cfg.GetClusterName()
9016
        for idx, dev in enumerate(snap_disks):
9017
          feedback_fn("Exporting snapshot %s from %s to %s" %
9018
                      (idx, src_node, dst_node.name))
9019
          if dev:
9020
            # FIXME: pass debug from opcode to backend
9021
            result = self.rpc.call_snapshot_export(src_node, dev, dst_node.name,
9022
                                                   instance, cluster_name,
9023
                                                   idx, self.op.debug_level)
9024
            msg = result.fail_msg
9025
            if msg:
9026
              self.LogWarning("Could not export disk/%s from node %s to"
9027
                              " node %s: %s", idx, src_node, dst_node.name, msg)
9028
              dresults.append(False)
9029
            else:
9030
              dresults.append(True)
9031

  
9032
            # Remove snapshot
9033
            if self._RemoveSnapshot(feedback_fn, snap_disks, idx):
9034
              removed_snaps[idx] = True
8954 9035
          else:
8955
            dresults.append(True)
8956
          msg = self.rpc.call_blockdev_remove(src_node, dev).fail_msg
8957
          if msg:
8958
            self.LogWarning("Could not remove snapshot for disk/%d from node"
8959
                            " %s: %s", idx, src_node, msg)
8960
        else:
8961
          dresults.append(False)
9036
            dresults.append(False)
8962 9037

  
8963
      feedback_fn("Finalizing export on %s" % dst_node.name)
8964
      result = self.rpc.call_finalize_export(dst_node.name, instance,
8965
                                             snap_disks)
8966
      fin_resu = True
8967
      msg = result.fail_msg
8968
      if msg:
8969
        self.LogWarning("Could not finalize export for instance %s"
8970
                        " on node %s: %s", instance.name, dst_node.name, msg)
8971
        fin_resu = False
9038
        assert len(dresults) == len(instance.disks)
9039

  
9040
        # Check for backwards compatibility
9041
        assert compat.all(isinstance(i, bool) for i in dresults), \
9042
               "Not all results are boolean: %r" % dresults
9043

  
9044
        feedback_fn("Finalizing export on %s" % dst_node.name)
9045
        result = self.rpc.call_finalize_export(dst_node.name, instance,
9046
                                               snap_disks)
9047
        msg = result.fail_msg
9048
        fin_resu = not msg
9049
        if msg:
9050
          self.LogWarning("Could not finalize export for instance %s"
9051
                          " on node %s: %s", instance.name, dst_node.name, msg)
9052

  
9053
      finally:
9054
        # Remove all snapshots
9055
        assert len(removed_snaps) == len(instance.disks)
9056
        for idx, removed in enumerate(removed_snaps):
9057
          if not removed:
9058
            self._RemoveSnapshot(feedback_fn, snap_disks, idx)
8972 9059

  
8973 9060
    finally:
8974 9061
      if activate_disks:
8975 9062
        feedback_fn("Deactivating disks for %s" % instance.name)
8976 9063
        _ShutdownInstanceDisks(self, instance)
8977 9064

  
8978
    nodelist = self.cfg.GetNodeList()
8979
    nodelist.remove(dst_node.name)
9065
    self._CleanupExports(feedback_fn)
8980 9066

  
8981
    # on one-node clusters nodelist will be empty after the removal
8982
    # if we proceed the backup would be removed because OpQueryExports
8983
    # substitutes an empty list with the full cluster node list.
8984
    iname = instance.name
8985
    if nodelist:
8986
      feedback_fn("Removing old exports for instance %s" % iname)
8987
      exportlist = self.rpc.call_export_list(nodelist)
8988
      for node in exportlist:
8989
        if exportlist[node].fail_msg:
8990
          continue
8991
        if iname in exportlist[node].payload:
8992
          msg = self.rpc.call_export_remove(node, iname).fail_msg
8993
          if msg:
8994
            self.LogWarning("Could not remove older export for instance %s"
8995
                            " on node %s: %s", iname, node, msg)
8996 9067
    return fin_resu, dresults
8997 9068

  
8998 9069

  

Also available in: Unified diff