diff --git a/lib/config.py b/lib/config.py
index 6af89c0e76ba40d879d0c93284c0ac29886d6c35..8c81eb0eb3346cdab22ce3a8b220858d61de8ec9 100644
--- a/lib/config.py
+++ b/lib/config.py
@@ -864,6 +864,58 @@ class ConfigWriter:
     """
     return self._config_data.cluster.primary_ip_family
 
+  @locking.ssynchronized(_config_lock)
+  def AddNodeGroup(self, group, ec_id, check_uuid=True):
+    """Add a node group to the configuration.
+
+    @type group: L{objects.NodeGroup}
+    @param group: the NodeGroup object to add
+    @type ec_id: string
+    @param ec_id: unique id for the job to use when creating a missing UUID
+    @type check_uuid: bool
+    @param check_uuid: add an UUID to the group if it doesn't have one or, if
+                       it does, ensure that it does not exist in the
+                       configuration already
+
+    """
+    self._UnlockedAddNodeGroup(group, ec_id, check_uuid)
+    self._WriteConfig()
+
+  def _UnlockedAddNodeGroup(self, group, ec_id, check_uuid):
+    """Add a node group to the configuration.
+
+    """
+    logging.info("Adding node group %s to configuration", group.name)
+
+    # Some code might need to add a node group with a pre-populated UUID
+    # generated with ConfigWriter.GenerateUniqueID(). We allow them to bypass
+    # the "does this UUID" exist already check.
+    if check_uuid:
+      self._EnsureUUID(group, ec_id)
+
+    group.serial_no = 1
+    group.ctime = group.mtime = time.time()
+
+    self._config_data.nodegroups[group.uuid] = group
+    self._config_data.cluster.serial_no += 1
+
+  @locking.ssynchronized(_config_lock)
+  def RemoveNodeGroup(self, group_uuid):
+    """Remove a node group from the configuration.
+
+    @type group_uuid: string
+    @param group_uuid: the UUID of the node group to remove
+
+    """
+    logging.info("Removing node group %s from configuration", group_uuid)
+
+    if group_uuid not in self._config_data.nodegroups:
+      raise errors.ConfigurationError("Unknown node group '%s'" % group_uuid)
+
+    del self._config_data.nodegroups[group_uuid]
+    self._config_data.cluster.serial_no += 1
+    self._WriteConfig()
+
   @locking.ssynchronized(_config_lock, shared=1)
   def LookupNodeGroup(self, target):
     """Lookup a node group's UUID.
@@ -1434,13 +1486,8 @@ class ConfigWriter:
         item.uuid = self._GenerateUniqueID(_UPGRADE_CONFIG_JID)
         modified = True
     if not self._config_data.nodegroups:
-      default_nodegroup_uuid = self._GenerateUniqueID(_UPGRADE_CONFIG_JID)
-      default_nodegroup = objects.NodeGroup(
-          uuid=default_nodegroup_uuid,
-          name="default",
-          members=[],
-          )
-      self._config_data.nodegroups[default_nodegroup_uuid] = default_nodegroup
+      default_nodegroup = objects.NodeGroup(name="default", members=[])
+      self._UnlockedAddNodeGroup(default_nodegroup, _UPGRADE_CONFIG_JID, True)
       modified = True
     for node in self._config_data.nodes.values():
       if not node.group:
@@ -1712,6 +1759,8 @@ class ConfigWriter:
       update_serial = True
     elif isinstance(target, objects.Instance):
       test = target in self._config_data.instances.values()
+    elif isinstance(target, objects.NodeGroup):
+      test = target in self._config_data.nodegroups.values()
     else:
       raise errors.ProgrammerError("Invalid object type (%s) passed to"
                                    " ConfigWriter.Update" % type(target))
diff --git a/lib/objects.py b/lib/objects.py
index 3d0951725efb08d764f2c4d5d48cfbb8489c28bf..c2804bc59f299cd38ecb65764abe06aa4458adc1 100644
--- a/lib/objects.py
+++ b/lib/objects.py
@@ -36,6 +36,7 @@ pass to and from external parties.
 import ConfigParser
 import re
 import copy
+import time
 from cStringIO import StringIO
 
 from ganeti import errors
@@ -954,6 +955,7 @@ class NodeGroup(ConfigObject):
     "name",
     "members",
     "ndparams",
+    "serial_no",
     ] + _TIMESTAMPS + _UUID
 
   def ToDict(self):
@@ -985,6 +987,14 @@ class NodeGroup(ConfigObject):
     if self.ndparams is None:
       self.ndparams = {}
 
+    if self.serial_no is None:
+      self.serial_no = 1
+
+    # We only update mtime, and not ctime, since we would not be able to provide
+    # a correct value for creation time.
+    if self.mtime is None:
+      self.mtime = time.time()
+
   def FillND(self, node):
     """Return filled out ndparams for L{object.Node}