diff --git a/lib/cmdlib.py b/lib/cmdlib.py
index 99b11b407901eca96d9bc00d34175713daa63faf..da13bd50b2c1e4f2519eb160932f139c6c7d2cc9 100644
--- a/lib/cmdlib.py
+++ b/lib/cmdlib.py
@@ -2064,6 +2064,25 @@ class LUSetClusterParams(LogicalUnit):
         else:
           self.new_hvparams[hv_name].update(hv_dict)
 
+    # os hypervisor parameters
+    self.new_os_hvp = objects.FillDict(cluster.os_hvp, {})
+    if self.op.os_hvp:
+      if not isinstance(self.op.os_hvp, dict):
+        raise errors.OpPrereqError("Invalid 'os_hvp' parameter on input",
+                                   errors.ECODE_INVAL)
+      for os_name, hvs in self.op.os_hvp.items():
+        if not isinstance(hvs, dict):
+          raise errors.OpPrereqError(("Invalid 'os_hvp' parameter on"
+                                      " input"), errors.ECODE_INVAL)
+        if os_name not in self.new_os_hvp:
+          self.new_os_hvp[os_name] = hvs
+        else:
+          for hv_name, hv_dict in hvs.items():
+            if hv_name not in self.new_os_hvp[os_name]:
+              self.new_os_hvp[os_name][hv_name] = hv_dict
+            else:
+              self.new_os_hvp[os_name][hv_name].update(hv_dict)
+
     if self.op.enabled_hypervisors is not None:
       self.hv_list = self.op.enabled_hypervisors
       if not self.hv_list:
@@ -2106,6 +2125,8 @@ class LUSetClusterParams(LogicalUnit):
                     " state, not changing")
     if self.op.hvparams:
       self.cluster.hvparams = self.new_hvparams
+    if self.op.os_hvp:
+      self.cluster.os_hvp = self.new_os_hvp
     if self.op.enabled_hypervisors is not None:
       self.cluster.enabled_hypervisors = self.op.enabled_hypervisors
     if self.op.beparams:
@@ -3336,6 +3357,15 @@ class LUQueryClusterInfo(NoHooksLU):
 
     """
     cluster = self.cfg.GetClusterInfo()
+    os_hvp = {}
+
+    # Filter just for enabled hypervisors
+    for os_name, hv_dict in cluster.os_hvp.items():
+      os_hvp[os_name] = {}
+      for hv_name, hv_params in hv_dict.items():
+        if hv_name in cluster.enabled_hypervisors:
+          os_hvp[os_name][hv_name] = hv_params
+
     result = {
       "software_version": constants.RELEASE_VERSION,
       "protocol_version": constants.PROTOCOL_VERSION,
@@ -3349,6 +3379,7 @@ class LUQueryClusterInfo(NoHooksLU):
       "enabled_hypervisors": cluster.enabled_hypervisors,
       "hvparams": dict([(hypervisor_name, cluster.hvparams[hypervisor_name])
                         for hypervisor_name in cluster.enabled_hypervisors]),
+      "os_hvp": os_hvp,
       "beparams": cluster.beparams,
       "nicparams": cluster.nicparams,
       "candidate_pool_size": cluster.candidate_pool_size,
diff --git a/lib/objects.py b/lib/objects.py
index 6e81a45ea6dbab70fb4d1e5abbb7765cf8092292..91174a06d5a29f270c77ff1c8e25f64aa6f9a1d5 100644
--- a/lib/objects.py
+++ b/lib/objects.py
@@ -858,6 +858,7 @@ class Cluster(TaggableObject):
     "file_storage_dir",
     "enabled_hypervisors",
     "hvparams",
+    "os_hvp",
     "beparams",
     "nicparams",
     "candidate_pool_size",
@@ -878,6 +879,10 @@ class Cluster(TaggableObject):
         self.hvparams[hypervisor] = FillDict(
             constants.HVC_DEFAULTS[hypervisor], self.hvparams[hypervisor])
 
+    # TODO: Figure out if it's better to put this into OS than Cluster
+    if self.os_hvp is None:
+      self.os_hvp = {}
+
     self.beparams = UpgradeGroupedParams(self.beparams,
                                          constants.BEC_DEFAULTS)
     migrate_default_bridge = not self.nicparams
@@ -940,8 +945,19 @@ class Cluster(TaggableObject):
       skip_keys = constants.HVC_GLOBALS
     else:
       skip_keys = []
-    return FillDict(self.hvparams.get(instance.hypervisor, {}),
-                    instance.hvparams, skip_keys=skip_keys)
+
+    # We fill the list from least to most important override
+    fill_stack = [
+      self.hvparams.get(instance.hypervisor, {}),
+      self.os_hvp.get(instance.os, {}).get(instance.hypervisor, {}),
+      instance.hvparams,
+      ]
+
+    ret_dict = {}
+    for o_dict in fill_stack:
+      ret_dict = FillDict(ret_dict, o_dict, skip_keys=skip_keys)
+
+    return ret_dict
 
   def FillBE(self, instance):
     """Fill an instance's beparams dict.
diff --git a/lib/opcodes.py b/lib/opcodes.py
index 66698f5fcb26a65d4766512754de6431bc9beda3..0799f8ca4c58b52c0cb2fc90fb74837ce6746afc 100644
--- a/lib/opcodes.py
+++ b/lib/opcodes.py
@@ -301,6 +301,7 @@ class OpSetClusterParams(OpCode):
     "vg_name",
     "enabled_hypervisors",
     "hvparams",
+    "os_hvp",
     "beparams",
     "nicparams",
     "candidate_pool_size",
diff --git a/scripts/gnt-cluster b/scripts/gnt-cluster
index 2a72811645d4f833bad5e1308f9686e1df2189c2..4a15d59fe9ec53fd98706f38d0bd833cd51c2567 100755
--- a/scripts/gnt-cluster
+++ b/scripts/gnt-cluster
@@ -527,6 +527,7 @@ def SetClusterParams(opts, args):
   op = opcodes.OpSetClusterParams(vg_name=vg_name,
                                   enabled_hypervisors=hvlist,
                                   hvparams=hvparams,
+                                  os_hvp=None,
                                   beparams=beparams,
                                   nicparams=nicparams,
                                   candidate_pool_size=opts.candidate_pool_size)