+ def _UpdateNodeOS(self, ninfo, nresult, nimg):
+ """Builds the node OS structures.
+
+ @type ninfo: L{objects.Node}
+ @param ninfo: the node to check
+ @param nresult: the remote results for the node
+ @param nimg: the node image object
+
+ """
+ node = ninfo.name
+ _ErrorIf = self._ErrorIf # pylint: disable-msg=C0103
+
+ remote_os = nresult.get(constants.NV_OSLIST, None)
+ test = (not isinstance(remote_os, list) or
+ not compat.all(isinstance(v, list) and len(v) == 7
+ for v in remote_os))
+
+ _ErrorIf(test, self.ENODEOS, node,
+ "node hasn't returned valid OS data")
+
+ nimg.os_fail = test
+
+ if test:
+ return
+
+ os_dict = {}
+
+ for (name, os_path, status, diagnose,
+ variants, parameters, api_ver) in nresult[constants.NV_OSLIST]:
+
+ if name not in os_dict:
+ os_dict[name] = []
+
+ # parameters is a list of lists instead of list of tuples due to
+ # JSON lacking a real tuple type, fix it:
+ parameters = [tuple(v) for v in parameters]
+ os_dict[name].append((os_path, status, diagnose,
+ set(variants), set(parameters), set(api_ver)))
+
+ nimg.oslist = os_dict
+
+ def _VerifyNodeOS(self, ninfo, nimg, base):
+ """Verifies the node OS list.
+
+ @type ninfo: L{objects.Node}
+ @param ninfo: the node to check
+ @param nimg: the node image object
+ @param base: the 'template' node we match against (e.g. from the master)
+
+ """
+ node = ninfo.name
+ _ErrorIf = self._ErrorIf # pylint: disable-msg=C0103
+
+ assert not nimg.os_fail, "Entered _VerifyNodeOS with failed OS rpc?"
+
+ for os_name, os_data in nimg.oslist.items():
+ assert os_data, "Empty OS status for OS %s?!" % os_name
+ f_path, f_status, f_diag, f_var, f_param, f_api = os_data[0]
+ _ErrorIf(not f_status, self.ENODEOS, node,
+ "Invalid OS %s (located at %s): %s", os_name, f_path, f_diag)
+ _ErrorIf(len(os_data) > 1, self.ENODEOS, node,
+ "OS '%s' has multiple entries (first one shadows the rest): %s",
+ os_name, utils.CommaJoin([v[0] for v in os_data]))
+ # this will catched in backend too
+ _ErrorIf(compat.any(v >= constants.OS_API_V15 for v in f_api)
+ and not f_var, self.ENODEOS, node,
+ "OS %s with API at least %d does not declare any variant",
+ os_name, constants.OS_API_V15)
+ # comparisons with the 'base' image
+ test = os_name not in base.oslist
+ _ErrorIf(test, self.ENODEOS, node,
+ "Extra OS %s not present on reference node (%s)",
+ os_name, base.name)
+ if test:
+ continue
+ assert base.oslist[os_name], "Base node has empty OS status?"
+ _, b_status, _, b_var, b_param, b_api = base.oslist[os_name][0]
+ if not b_status:
+ # base OS is invalid, skipping
+ continue
+ for kind, a, b in [("API version", f_api, b_api),
+ ("variants list", f_var, b_var),
+ ("parameters", f_param, b_param)]:
+ _ErrorIf(a != b, self.ENODEOS, node,
+ "OS %s %s differs from reference node %s: %s vs. %s",
+ kind, os_name, base.name,
+ utils.CommaJoin(a), utils.CommaJoin(b))
+
+ # check any missing OSes
+ missing = set(base.oslist.keys()).difference(nimg.oslist.keys())
+ _ErrorIf(missing, self.ENODEOS, node,
+ "OSes present on reference node %s but missing on this node: %s",
+ base.name, utils.CommaJoin(missing))
+