+ def _CollectDiskInfo(self, nodelist, node_image, instanceinfo):
+ """Gets per-disk status information for all instances.
+
+ @type nodelist: list of strings
+ @param nodelist: Node names
+ @type node_image: dict of (name, L{objects.Node})
+ @param node_image: Node objects
+ @type instanceinfo: dict of (name, L{objects.Instance})
+ @param instanceinfo: Instance objects
+ @rtype: {instance: {node: [(succes, payload)]}}
+ @return: a dictionary of per-instance dictionaries with nodes as
+ keys and disk information as values; the disk information is a
+ list of tuples (success, payload)
+
+ """
+ _ErrorIf = self._ErrorIf # pylint: disable-msg=C0103
+
+ node_disks = {}
+ node_disks_devonly = {}
+ diskless_instances = set()
+ diskless = constants.DT_DISKLESS
+
+ for nname in nodelist:
+ node_instances = list(itertools.chain(node_image[nname].pinst,
+ node_image[nname].sinst))
+ diskless_instances.update(inst for inst in node_instances
+ if instanceinfo[inst].disk_template == diskless)
+ disks = [(inst, disk)
+ for inst in node_instances
+ for disk in instanceinfo[inst].disks]
+
+ if not disks:
+ # No need to collect data
+ continue
+
+ node_disks[nname] = disks
+
+ # Creating copies as SetDiskID below will modify the objects and that can
+ # lead to incorrect data returned from nodes
+ devonly = [dev.Copy() for (_, dev) in disks]
+
+ for dev in devonly:
+ self.cfg.SetDiskID(dev, nname)
+
+ node_disks_devonly[nname] = devonly
+
+ assert len(node_disks) == len(node_disks_devonly)
+
+ # Collect data from all nodes with disks
+ result = self.rpc.call_blockdev_getmirrorstatus_multi(node_disks.keys(),
+ node_disks_devonly)
+
+ assert len(result) == len(node_disks)
+
+ instdisk = {}
+
+ for (nname, nres) in result.items():
+ disks = node_disks[nname]
+
+ if nres.offline:
+ # No data from this node
+ data = len(disks) * [(False, "node offline")]
+ else:
+ msg = nres.fail_msg
+ _ErrorIf(msg, self.ENODERPC, nname,
+ "while getting disk information: %s", msg)
+ if msg:
+ # No data from this node
+ data = len(disks) * [(False, msg)]
+ else:
+ data = []
+ for idx, i in enumerate(nres.payload):
+ if isinstance(i, (tuple, list)) and len(i) == 2:
+ data.append(i)
+ else:
+ logging.warning("Invalid result from node %s, entry %d: %s",
+ nname, idx, i)
+ data.append((False, "Invalid result from the remote node"))
+
+ for ((inst, _), status) in zip(disks, data):
+ instdisk.setdefault(inst, {}).setdefault(nname, []).append(status)
+
+ # Add empty entries for diskless instances.
+ for inst in diskless_instances:
+ assert inst not in instdisk
+ instdisk[inst] = {}
+
+ assert compat.all(len(statuses) == len(instanceinfo[inst].disks) and
+ len(nnames) <= len(instanceinfo[inst].all_nodes) and
+ compat.all(isinstance(s, (tuple, list)) and
+ len(s) == 2 for s in statuses)
+ for inst, nnames in instdisk.items()
+ for nname, statuses in nnames.items())
+ assert set(instdisk) == set(instanceinfo), "instdisk consistency failure"
+
+ return instdisk
+