diff --git a/autotools/check-news b/autotools/check-news
index 9a2299526081b178f7d735e022cc7a721743c5a3..146faf2ea6229bb73b62b8f39eefd57c0e7f894d 100755
--- a/autotools/check-news
+++ b/autotools/check-news
@@ -1,7 +1,7 @@
 #!/usr/bin/python
 #
 
-# Copyright (C) 2011, 2012 Google Inc.
+# Copyright (C) 2011, 2012, 2013 Google Inc.
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -40,6 +40,9 @@ RELEASED_RE = re.compile(r"^\*\(Released (?P<day>[A-Z][a-z]{2}),"
 UNRELEASED_RE = re.compile(r"^\*\(unreleased\)\*$")
 VERSION_RE = re.compile(r"^Version \d+(\.\d+)+( (beta|rc)\d+)?$")
 
+#: How many days release timestamps may be in the future
+TIMESTAMP_FUTURE_DAYS_MAX = 3
+
 errors = []
 
 
@@ -115,6 +118,13 @@ def main():
       # would return an inconsistent result if the weekday is incorrect.
       parsed_ts = time.mktime(time.strptime(m.group("date"), "%d %b %Y"))
       parsed = datetime.date.fromtimestamp(parsed_ts)
+      today = datetime.date.today()
+
+      if (parsed - datetime.timedelta(TIMESTAMP_FUTURE_DAYS_MAX)) > today:
+        Error("Line %s: %s is more than %s days in the future (today is %s)" %
+              (fileinput.filelineno(), parsed, TIMESTAMP_FUTURE_DAYS_MAX,
+               today))
+
       weekday = parsed.strftime("%a")
 
       # Check weekday
diff --git a/autotools/convert-constants b/autotools/convert-constants
index c9695f4fd332656c5704696eca0f38a18405696a..e5296ea78ee80f6dc66213b7a5495640a906acfe 100755
--- a/autotools/convert-constants
+++ b/autotools/convert-constants
@@ -1,7 +1,7 @@
 #!/usr/bin/python
 #
 
-# Copyright (C) 2011, 2012 Google Inc.
+# Copyright (C) 2011, 2012, 2013 Google Inc.
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -87,6 +87,8 @@ def HaskellTypeVal(value):
   """
   if isinstance(value, basestring):
     return ("String", "\"%s\"" % StringValueRules(value))
+  elif isinstance(value, bool):
+    return ("Bool", "%s" % value)
   elif isinstance(value, int):
     return ("Int", "%d" % value)
   elif isinstance(value, long):
diff --git a/doc/move-instance.rst b/doc/move-instance.rst
index 6e616fb6aa1c0c22e3d64ace1e0b34167fc0e4fb..a7f06b7000419155527c365f1ef351b76b7766a6 100644
--- a/doc/move-instance.rst
+++ b/doc/move-instance.rst
@@ -69,9 +69,10 @@ destination-related options default to the source value (e.g. setting
 ``--src-ca-file``/``--dest-ca-file``
   Path to file containing source cluster Certificate Authority (CA) in
   PEM format. For self-signed certificates, this is the certificate
-  itself. For certificates signed by a third party CA, the complete
-  chain must be in the file (see documentation for
-  :manpage:`SSL_CTX_load_verify_locations(3)`).
+  itself (see more details below in
+  :ref:`instance-move-certificates`). For certificates signed by a third
+  party CA, the complete chain must be in the file (see documentation
+  for :manpage:`SSL_CTX_load_verify_locations(3)`).
 ``--src-username``/``--dest-username``
   RAPI username, must have write access to cluster.
 ``--src-password-file``/``--dest-password-file``
@@ -96,6 +97,28 @@ destination-related options default to the source value (e.g. setting
 The exit value of the tool is zero if and only if all instance moves
 were successful.
 
+.. _instance-move-certificates:
+
+Certificates
+------------
+
+If using certificates signed by a CA, then you need to pass the same CA
+certificate via both ``--src-ca-file`` and ``dest-ca-file``.
+
+However, if you're using self-signed certificates, this has a few
+(security) implications:
+
+- the certificates of both the source and destinations clusters
+  (``rapi.pem`` from the Ganeti configuration directory, usually
+  ``/var/lib/ganeti/rapi.pem``) must be available to the tool
+- by default, the certificates include the private key as well, so
+  simply copying them to a third machine means that machine can now
+  impersonate both the source and destination clusters RAPI endpoint
+
+It is therefore recommended to copy only the certificate from the
+``rapi.pem`` files, and pass these to ``--src-ca-file`` and
+``--dest-ca-file`` appropriately.
+
 .. vim: set textwidth=72 :
 .. Local Variables:
 .. mode: rst
diff --git a/lib/bdev.py b/lib/bdev.py
index 080880cafa017992cbfb7a162b6b12441f84fc1d..3c453d0f133748c67d8f58ccec1f45c7cba2ae8c 100644
--- a/lib/bdev.py
+++ b/lib/bdev.py
@@ -1,7 +1,7 @@
 #
 #
 
-# Copyright (C) 2006, 2007, 2010, 2011, 2012 Google Inc.
+# Copyright (C) 2006, 2007, 2010, 2011, 2012, 2013 Google Inc.
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -255,7 +255,7 @@ class BlockDev(object):
       an attached instance (lvcreate)
     - attaching of a python instance to an existing (real) device
 
-  The second point, the attachement to a device, is different
+  The second point, the attachment to a device, is different
   depending on whether the device is assembled or not. At init() time,
   we search for a device with the same unique_id as us. If found,
   good. It also means that the device is already assembled. If not,
diff --git a/lib/cmdlib.py b/lib/cmdlib.py
index b8c2318cab736f0094867d1767137688c96fedcd..13eddd000f532dc8fb7f7b96993f9fb5b8fedb4a 100644
--- a/lib/cmdlib.py
+++ b/lib/cmdlib.py
@@ -1,7 +1,7 @@
 #
 #
 
-# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Google Inc.
+# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Google Inc.
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -13497,23 +13497,28 @@ class LUInstanceSetParams(LogicalUnit):
         params[constants.INIC_MAC] = \
           self.cfg.GenerateMAC(new_net_uuid, self.proc.GetECId())
 
-    #if there is a change in nic's ip/network configuration
+    # if there is a change in (ip, network) tuple
     new_ip = params.get(constants.INIC_IP, old_ip)
     if (new_ip, new_net_uuid) != (old_ip, old_net_uuid):
       if new_ip:
+        # if IP is pool then require a network and generate one IP
         if new_ip.lower() == constants.NIC_IP_POOL:
-          if not new_net_uuid:
+          if new_net_uuid:
+            try:
+              new_ip = self.cfg.GenerateIp(new_net_uuid, self.proc.GetECId())
+            except errors.ReservationError:
+              raise errors.OpPrereqError("Unable to get a free IP"
+                                         " from the address pool",
+                                         errors.ECODE_STATE)
+            self.LogInfo("Chose IP %s from network %s",
+                         new_ip,
+                         new_net_obj.name)
+            params[constants.INIC_IP] = new_ip
+          else:
             raise errors.OpPrereqError("ip=pool, but no network found",
                                        errors.ECODE_INVAL)
-          try:
-            new_ip = self.cfg.GenerateIp(new_net_uuid, self.proc.GetECId())
-          except errors.ReservationError:
-            raise errors.OpPrereqError("Unable to get a free IP"
-                                       " from the address pool",
-                                       errors.ECODE_STATE)
-          self.LogInfo("Chose IP %s from network %s", new_ip, new_net_obj.name)
-          params[constants.INIC_IP] = new_ip
-        elif new_ip != old_ip or new_net_uuid != old_net_uuid:
+        # Reserve new IP if in the new network if any
+        elif new_net_uuid:
           try:
             self.cfg.ReserveIp(new_net_uuid, new_ip, self.proc.GetECId())
             self.LogInfo("Reserving IP %s in network %s",
@@ -13522,19 +13527,19 @@ class LUInstanceSetParams(LogicalUnit):
             raise errors.OpPrereqError("IP %s not available in network %s" %
                                        (new_ip, new_net_obj.name),
                                        errors.ECODE_NOTUNIQUE)
-
-        # new net is None
-        elif not new_net_uuid and self.op.conflicts_check:
+        # new network is None so check if new IP is a conflicting IP
+        elif self.op.conflicts_check:
           _CheckForConflictingIp(self, new_ip, pnode)
 
-      if old_ip:
+      # release old IP if old network is not None
+      if old_ip and old_net_uuid:
         try:
           self.cfg.ReleaseIp(old_net_uuid, old_ip, self.proc.GetECId())
         except errors.AddressPoolError:
           logging.warning("Release IP %s not contained in network %s",
                           old_ip, old_net_obj.name)
 
-    # there are no changes in (net, ip) tuple
+    # there are no changes in (ip, network) tuple and old network is not None
     elif (old_net_uuid is not None and
           (req_link is not None or req_mode is not None)):
       raise errors.OpPrereqError("Not allowed to change link or mode of"
@@ -14112,18 +14117,19 @@ class LUInstanceSetParams(LogicalUnit):
     if root.dev_type in constants.LDS_DRBD:
       self.cfg.AddTcpUdpPort(root.logical_id[2])
 
-  @staticmethod
-  def _CreateNewNic(idx, params, private):
+  def _CreateNewNic(self, idx, params, private):
     """Creates data structure for a new network interface.
 
     """
     mac = params[constants.INIC_MAC]
     ip = params.get(constants.INIC_IP, None)
     net = params.get(constants.INIC_NETWORK, None)
+    net_uuid = self.cfg.LookupNetwork(net)
     #TODO: not private.filled?? can a nic have no nicparams??
     nicparams = private.filled
+    nobj = objects.NIC(mac=mac, ip=ip, network=net_uuid, nicparams=nicparams)
 
-    return (objects.NIC(mac=mac, ip=ip, network=net, nicparams=nicparams), [
+    return (nobj, [
       ("nic.%d" % idx,
        "add:mac=%s,ip=%s,mode=%s,link=%s,network=%s" %
        (mac, ip, private.filled[constants.NIC_MODE],
@@ -14131,18 +14137,23 @@ class LUInstanceSetParams(LogicalUnit):
        net)),
       ])
 
-  @staticmethod
-  def _ApplyNicMods(idx, nic, params, private):
+  def _ApplyNicMods(self, idx, nic, params, private):
     """Modifies a network interface.
 
     """
     changes = []
 
-    for key in [constants.INIC_MAC, constants.INIC_IP, constants.INIC_NETWORK]:
+    for key in [constants.INIC_MAC, constants.INIC_IP]:
       if key in params:
         changes.append(("nic.%s/%d" % (key, idx), params[key]))
         setattr(nic, key, params[key])
 
+    new_net = params.get(constants.INIC_NETWORK, nic.network)
+    new_net_uuid = self.cfg.LookupNetwork(new_net)
+    if new_net_uuid != nic.network:
+      changes.append(("nic.network/%d" % idx, new_net))
+      nic.network = new_net_uuid
+
     if private.filled:
       nic.nicparams = private.filled
 
@@ -16166,7 +16177,8 @@ class LUTestAllocator(NoHooksLU):
                                           nics=self.op.nics,
                                           vcpus=self.op.vcpus,
                                           spindle_use=self.op.spindle_use,
-                                          hypervisor=self.op.hypervisor)
+                                          hypervisor=self.op.hypervisor,
+                                          node_whitelist=None)
     elif self.op.mode == constants.IALLOCATOR_MODE_RELOC:
       req = iallocator.IAReqRelocate(name=self.op.name,
                                      relocate_from=list(self.relocate_from))
@@ -16702,6 +16714,11 @@ class LUNetworkConnect(LogicalUnit):
 
     assert self.group_uuid in owned_groups
 
+    # Check if locked instances are still correct
+    owned_instances = frozenset(self.owned_locks(locking.LEVEL_INSTANCE))
+    if self.op.conflicts_check:
+      _CheckNodeGroupInstances(self.cfg, self.group_uuid, owned_instances)
+
     self.netparams = {
       constants.NIC_MODE: self.network_mode,
       constants.NIC_LINK: self.network_link,
@@ -16716,23 +16733,22 @@ class LUNetworkConnect(LogicalUnit):
       self.LogWarning("Network '%s' is already mapped to group '%s'" %
                       (self.network_name, self.group.name))
       self.connected = True
-      return
 
-    if self.op.conflicts_check:
+    # check only if not already connected
+    elif self.op.conflicts_check:
       pool = network.AddressPool(self.cfg.GetNetwork(self.network_uuid))
 
       _NetworkConflictCheck(self, lambda nic: pool.Contains(nic.ip),
-                            "connect to")
+                            "connect to", owned_instances)
 
   def Exec(self, feedback_fn):
-    if self.connected:
-      return
-
-    self.group.networks[self.network_uuid] = self.netparams
-    self.cfg.Update(self.group, feedback_fn)
+    # Connect the network and update the group only if not already connected
+    if not self.connected:
+      self.group.networks[self.network_uuid] = self.netparams
+      self.cfg.Update(self.group, feedback_fn)
 
 
-def _NetworkConflictCheck(lu, check_fn, action):
+def _NetworkConflictCheck(lu, check_fn, action, instances):
   """Checks for network interface conflicts with a network.
 
   @type lu: L{LogicalUnit}
@@ -16744,13 +16760,9 @@ def _NetworkConflictCheck(lu, check_fn, action):
   @raise errors.OpPrereqError: If conflicting IP addresses are found.
 
   """
-  # Check if locked instances are still correct
-  owned_instances = frozenset(lu.owned_locks(locking.LEVEL_INSTANCE))
-  _CheckNodeGroupInstances(lu.cfg, lu.group_uuid, owned_instances)
-
   conflicts = []
 
-  for (_, instance) in lu.cfg.GetMultiInstanceInfo(owned_instances):
+  for (_, instance) in lu.cfg.GetMultiInstanceInfo(instances):
     instconflicts = [(idx, nic.ip)
                      for (idx, nic) in enumerate(instance.nics)
                      if check_fn(nic)]
@@ -16824,23 +16836,27 @@ class LUNetworkDisconnect(LogicalUnit):
 
     assert self.group_uuid in owned_groups
 
+    # Check if locked instances are still correct
+    owned_instances = frozenset(self.owned_locks(locking.LEVEL_INSTANCE))
+    _CheckNodeGroupInstances(self.cfg, self.group_uuid, owned_instances)
+
     self.group = self.cfg.GetNodeGroup(self.group_uuid)
     self.connected = True
     if self.network_uuid not in self.group.networks:
       self.LogWarning("Network '%s' is not mapped to group '%s'",
                       self.network_name, self.group.name)
       self.connected = False
-      return
 
-    _NetworkConflictCheck(self, lambda nic: nic.network == self.network_uuid,
-                          "disconnect from")
+    # We need this check only if network is not already connected
+    else:
+      _NetworkConflictCheck(self, lambda nic: nic.network == self.network_uuid,
+                            "disconnect from", owned_instances)
 
   def Exec(self, feedback_fn):
-    if not self.connected:
-      return
-
-    del self.group.networks[self.network_uuid]
-    self.cfg.Update(self.group, feedback_fn)
+    # Disconnect the network and update the group only if network is connected
+    if self.connected:
+      del self.group.networks[self.network_uuid]
+      self.cfg.Update(self.group, feedback_fn)
 
 
 #: Query type implementations
diff --git a/lib/config.py b/lib/config.py
index 13f9823604416439c49f819a866ff298a2b65890..93ccec8bbb00618ccdd54168dcc93f95ebff29d3 100644
--- a/lib/config.py
+++ b/lib/config.py
@@ -2021,6 +2021,7 @@ class ConfigWriter:
     return (self._config_data.instances.values() +
             self._config_data.nodes.values() +
             self._config_data.nodegroups.values() +
+            self._config_data.networks.values() +
             [self._config_data.cluster])
 
   def _OpenConfig(self, accept_foreign):
diff --git a/lib/masterd/iallocator.py b/lib/masterd/iallocator.py
index d5762b669c02adf33873a955800a5a71f28a8e4f..7179b0ea6de5715c2c78d21cef711cbbf9c4a291 100644
--- a/lib/masterd/iallocator.py
+++ b/lib/masterd/iallocator.py
@@ -431,7 +431,14 @@ class IAllocator(object):
       node_whitelist = None
 
     es_flags = rpc.GetExclusiveStorageForNodeNames(cfg, node_list)
-    node_data = self.rpc.call_node_info(node_list, [cfg.GetVGName()],
+    vg_name = cfg.GetVGName()
+    if vg_name is not None:
+      has_lvm = True
+      vg_req = [vg_name]
+    else:
+      has_lvm = False
+      vg_req = []
+    node_data = self.rpc.call_node_info(node_list, vg_req,
                                         [hypervisor_name], es_flags)
     node_iinfo = \
       self.rpc.call_all_instances_info(node_list,
@@ -441,7 +448,7 @@ class IAllocator(object):
 
     config_ndata = self._ComputeBasicNodeData(cfg, ninfo, node_whitelist)
     data["nodes"] = self._ComputeDynamicNodeData(ninfo, node_data, node_iinfo,
-                                                 i_list, config_ndata)
+                                                 i_list, config_ndata, has_lvm)
     assert len(data["nodes"]) == len(ninfo), \
         "Incomplete node data computed"
 
@@ -494,7 +501,7 @@ class IAllocator(object):
 
   @staticmethod
   def _ComputeDynamicNodeData(node_cfg, node_data, node_iinfo, i_list,
-                              node_results):
+                              node_results, has_lvm):
     """Compute global node data.
 
     @param node_results: the basic node structures as filled from the config
@@ -511,17 +518,22 @@ class IAllocator(object):
         nresult.Raise("Can't get data for node %s" % nname)
         node_iinfo[nname].Raise("Can't get node instance info from node %s" %
                                 nname)
-        remote_info = rpc.MakeLegacyNodeInfo(nresult.payload)
+        remote_info = rpc.MakeLegacyNodeInfo(nresult.payload,
+                                             require_vg_info=has_lvm)
 
-        for attr in ["memory_total", "memory_free", "memory_dom0",
-                     "vg_size", "vg_free", "cpu_total"]:
+        def get_attr(attr):
           if attr not in remote_info:
             raise errors.OpExecError("Node '%s' didn't return attribute"
                                      " '%s'" % (nname, attr))
-          if not isinstance(remote_info[attr], int):
+          value = remote_info[attr]
+          if not isinstance(value, int):
             raise errors.OpExecError("Node '%s' returned invalid value"
                                      " for '%s': %s" %
-                                     (nname, attr, remote_info[attr]))
+                                     (nname, attr, value))
+          return value
+
+        mem_free = get_attr("memory_free")
+
         # compute memory used by primary instances
         i_p_mem = i_p_up_mem = 0
         for iinfo, beinfo in i_list:
@@ -532,19 +544,27 @@ class IAllocator(object):
             else:
               i_used_mem = int(node_iinfo[nname].payload[iinfo.name]["memory"])
             i_mem_diff = beinfo[constants.BE_MAXMEM] - i_used_mem
-            remote_info["memory_free"] -= max(0, i_mem_diff)
+            mem_free -= max(0, i_mem_diff)
 
             if iinfo.admin_state == constants.ADMINST_UP:
               i_p_up_mem += beinfo[constants.BE_MAXMEM]
 
+        # TODO: replace this with proper storage reporting
+        if has_lvm:
+          total_disk = get_attr("vg_size")
+          free_disk = get_attr("vg_free")
+        else:
+          # we didn't even ask the node for VG status, so use zeros
+          total_disk = free_disk = 0
+
         # compute memory used by instances
         pnr_dyn = {
-          "total_memory": remote_info["memory_total"],
-          "reserved_memory": remote_info["memory_dom0"],
-          "free_memory": remote_info["memory_free"],
-          "total_disk": remote_info["vg_size"],
-          "free_disk": remote_info["vg_free"],
-          "total_cpus": remote_info["cpu_total"],
+          "total_memory": get_attr("memory_total"),
+          "reserved_memory": get_attr("memory_dom0"),
+          "free_memory": mem_free,
+          "total_disk": total_disk,
+          "free_disk": free_disk,
+          "total_cpus": get_attr("cpu_total"),
           "i_pri_memory": i_p_mem,
           "i_pri_up_memory": i_p_up_mem,
           }
diff --git a/lib/objects.py b/lib/objects.py
index fa811a62119da0495a53cfc90c2f39b050dadfcf..e2f9a1a0bba479fb77b5de1864081c6bba7144b5 100644
--- a/lib/objects.py
+++ b/lib/objects.py
@@ -2027,7 +2027,7 @@ class Network(TaggableObject):
     result = {
       "%sNETWORK_NAME" % prefix: self.name,
       "%sNETWORK_UUID" % prefix: self.uuid,
-      "%sNETWORK_TAGS" % prefix: " ".join(self.tags),
+      "%sNETWORK_TAGS" % prefix: " ".join(self.GetTags()),
     }
     if self.network:
       result["%sNETWORK_SUBNET" % prefix] = self.network
diff --git a/lib/pathutils.py b/lib/pathutils.py
index 8affef7439ec48abed685316cd9fa5d6931cef69..540148d0df310829e05f689ea85f928a79ef998c 100644
--- a/lib/pathutils.py
+++ b/lib/pathutils.py
@@ -1,7 +1,7 @@
 #
 #
 
-# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Google Inc.
+# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Google Inc.
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -29,9 +29,15 @@ from ganeti import vcluster
 
 
 # Build-time constants
-DEFAULT_FILE_STORAGE_DIR = vcluster.AddNodePrefix(_autoconf.FILE_STORAGE_DIR)
-DEFAULT_SHARED_FILE_STORAGE_DIR = \
-  vcluster.AddNodePrefix(_autoconf.SHARED_FILE_STORAGE_DIR)
+if _autoconf.ENABLE_FILE_STORAGE:
+  DEFAULT_FILE_STORAGE_DIR = vcluster.AddNodePrefix(_autoconf.FILE_STORAGE_DIR)
+else:
+  DEFAULT_FILE_STORAGE_DIR = _autoconf.FILE_STORAGE_DIR
+if _autoconf.ENABLE_SHARED_FILE_STORAGE:
+  DEFAULT_SHARED_FILE_STORAGE_DIR = \
+    vcluster.AddNodePrefix(_autoconf.SHARED_FILE_STORAGE_DIR)
+else:
+  DEFAULT_SHARED_FILE_STORAGE_DIR = _autoconf.SHARED_FILE_STORAGE_DIR
 EXPORT_DIR = vcluster.AddNodePrefix(_autoconf.EXPORT_DIR)
 OS_SEARCH_PATH = _autoconf.OS_SEARCH_PATH
 ES_SEARCH_PATH = _autoconf.ES_SEARCH_PATH
diff --git a/lib/rpc.py b/lib/rpc.py
index 0c4bad0792c7117c1c17879392f540e02042aa6a..f6da48984d65402de0e495d35842b4ef71a64090 100644
--- a/lib/rpc.py
+++ b/lib/rpc.py
@@ -1,7 +1,7 @@
 #
 #
 
-# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Google Inc.
+# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Google Inc.
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -571,18 +571,25 @@ def _EncodeBlockdevRename(value):
   return [(d.ToDict(), uid) for d, uid in value]
 
 
-def MakeLegacyNodeInfo(data):
+def MakeLegacyNodeInfo(data, require_vg_info=True):
   """Formats the data returned by L{rpc.RpcRunner.call_node_info}.
 
   Converts the data into a single dictionary. This is fine for most use cases,
   but some require information from more than one volume group or hypervisor.
 
+  @param require_vg_info: raise an error if the returnd vg_info
+      doesn't have any values
+
   """
-  (bootid, (vg_info, ), (hv_info, )) = data
+  (bootid, vgs_info, (hv_info, )) = data
+
+  ret = utils.JoinDisjointDicts(hv_info, {"bootid": bootid})
+
+  if require_vg_info or vgs_info:
+    (vg0_info, ) = vgs_info
+    ret = utils.JoinDisjointDicts(vg0_info, ret)
 
-  return utils.JoinDisjointDicts(utils.JoinDisjointDicts(vg_info, hv_info), {
-    "bootid": bootid,
-    })
+  return ret
 
 
 def _AnnotateDParamsDRBD(disk, (drbd_params, data_params, meta_params)):
diff --git a/qa/qa_node.py b/qa/qa_node.py
index 809f8795a39275554af0c0f11dc1af2af26c8409..d99004acdeaca56fee9018773abb021cdacc17fc 100644
--- a/qa/qa_node.py
+++ b/qa/qa_node.py
@@ -1,7 +1,7 @@
 #
 #
 
-# Copyright (C) 2007, 2011, 2012 Google Inc.
+# Copyright (C) 2007, 2011, 2012, 2013 Google Inc.
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -115,8 +115,18 @@ def TestNodeStorage():
   master = qa_config.GetMasterNode()
 
   for storage_type in constants.VALID_STORAGE_TYPES:
+
+    cmd = ["gnt-node", "list-storage", "--storage-type", storage_type]
+
+    # Skip file storage if not enabled, otherwise QA will fail; we
+    # just test for basic failure, but otherwise skip the rest of the
+    # tests
+    if storage_type == constants.ST_FILE and not constants.ENABLE_FILE_STORAGE:
+      AssertCommand(cmd, fail=True)
+      continue
+
     # Test simple list
-    AssertCommand(["gnt-node", "list-storage", "--storage-type", storage_type])
+    AssertCommand(cmd)
 
     # Test all storage fields
     cmd = ["gnt-node", "list-storage", "--storage-type", storage_type,
diff --git a/src/Ganeti/HTools/Program/Hbal.hs b/src/Ganeti/HTools/Program/Hbal.hs
index be10c1a5a250ee0fa5a6cf646ab10c96ac31ebab..26b1e3c83a1618007cc835941342b1684516bffa 100644
--- a/src/Ganeti/HTools/Program/Hbal.hs
+++ b/src/Ganeti/HTools/Program/Hbal.hs
@@ -319,7 +319,7 @@ checkGroup verbose gname nl il = do
   putStrLn $ "Selected node group: " ++ gname
 
   let (bad_nodes, bad_instances) = Cluster.computeBadItems nl il
-  unless (verbose == 0) $ printf
+  unless (verbose < 1) $ printf
              "Initial check done: %d bad nodes, %d bad instances.\n"
              (length bad_nodes) (length bad_instances)
 
@@ -399,7 +399,7 @@ main opts args = do
 
   putStr sol_msg
 
-  unless (verbose == 0) $
+  unless (verbose < 1) $
          printf "Solution length=%d\n" (length ord_plc)
 
   let cmd_jobs = Cluster.splitJobs cmd_strs
diff --git a/src/Ganeti/HTools/Program/Hcheck.hs b/src/Ganeti/HTools/Program/Hcheck.hs
index b86dc5de7d3f7059e51326856e3e82951a1bc2e4..35f32efbf466405cb691cbe6744b571609798879 100644
--- a/src/Ganeti/HTools/Program/Hcheck.hs
+++ b/src/Ganeti/HTools/Program/Hcheck.hs
@@ -4,7 +4,7 @@
 
 {-
 
-Copyright (C) 2012 Google Inc.
+Copyright (C) 2012, 2013 Google Inc.
 
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -191,7 +191,7 @@ printStats _ True level phase values = do
 printStats verbose False level phase values = do
   let prefix = phaseLevelDescr phase level
       descr = descrData level
-  unless (verbose == 0) $ do
+  unless (verbose < 1) $ do
     putStrLn ""
     putStr prefix
     mapM_ (uncurry (printf "    %s: %s\n")) (zip descr values)
@@ -311,7 +311,7 @@ main opts args = do
       clusterstats = map sum . transpose . map snd $ groupsstats
       needrebalance = clusterNeedsRebalance clusterstats
 
-  unless (verbose == 0 || machineread) .
+  unless (verbose < 1 || machineread) .
     putStrLn $ if nosimulation
                  then "Running in no-simulation mode."
                  else if needrebalance
diff --git a/src/Ganeti/Objects.hs b/src/Ganeti/Objects.hs
index 6e568e63c89afd757a360b9abc2d57dc37d179a1..bb7f84157f46a931464e206f29e2fa1a3d53212c 100644
--- a/src/Ganeti/Objects.hs
+++ b/src/Ganeti/Objects.hs
@@ -578,7 +578,8 @@ $(buildObject "Cluster" "cluster" $
   , simpleField "highest_used_port"       [t| Int              |]
   , simpleField "tcpudp_port_pool"        [t| [Int]            |]
   , simpleField "mac_prefix"              [t| String           |]
-  , simpleField "volume_group_name"       [t| String           |]
+  , optionalField $
+    simpleField "volume_group_name"       [t| String           |]
   , simpleField "reserved_lvs"            [t| [String]         |]
   , optionalField $
     simpleField "drbd_usermode_helper"    [t| String           |]
diff --git a/src/Ganeti/Query/Node.hs b/src/Ganeti/Query/Node.hs
index e639351ab1e9aaee327fcfd975fe9aa9ef525022..77eef3ec6c07f3824d2dd74bda1660256bfdace2 100644
--- a/src/Ganeti/Query/Node.hs
+++ b/src/Ganeti/Query/Node.hs
@@ -31,6 +31,7 @@ module Ganeti.Query.Node
 
 import Control.Applicative
 import Data.List
+import Data.Maybe
 import qualified Data.Map as Map
 import qualified Text.JSON as J
 
@@ -109,7 +110,7 @@ nodeLiveFieldBuilder (fname, ftitle, ftype, _, fdoc) =
   , FieldRuntime $ nodeLiveRpcCall fname
   , QffNormal)
 
--- | The docstring for the node role. Note that we use 'reverse in
+-- | The docstring for the node role. Note that we use 'reverse' in
 -- order to keep the same order as Python.
 nodeRoleDoc :: String
 nodeRoleDoc =
@@ -221,7 +222,7 @@ collectLiveData:: Bool -> ConfigData -> [Node] -> IO [(Node, Runtime)]
 collectLiveData False _ nodes =
   return $ zip nodes (repeat $ Left (RpcResultError "Live data disabled"))
 collectLiveData True cfg nodes = do
-  let vgs = [clusterVolumeGroupName $ configCluster cfg]
+  let vgs = maybeToList . clusterVolumeGroupName $ configCluster cfg
       hvs = [getDefaultHypervisor cfg]
       step n (bn, gn, em) =
         let ndp' = getNodeNdParams cfg n
diff --git a/src/Ganeti/Query/Server.hs b/src/Ganeti/Query/Server.hs
index 07fbce0fd869d96cfb492ba3f8f63c7db381a2aa..9176a1a1f0ee899134b9c5016fbe3a88dbb98830 100644
--- a/src/Ganeti/Query/Server.hs
+++ b/src/Ganeti/Query/Server.hs
@@ -116,7 +116,8 @@ handleCall cdata QueryClusterInfo =
             , ("master_netmask", showJSON $ clusterMasterNetmask cluster)
             , ("use_external_mip_script",
                showJSON $ clusterUseExternalMipScript cluster)
-            , ("volume_group_name", showJSON $ clusterVolumeGroupName cluster)
+            , ("volume_group_name",
+               maybe JSNull showJSON (clusterVolumeGroupName cluster))
             , ("drbd_usermode_helper",
                maybe JSNull showJSON (clusterDrbdUsermodeHelper cluster))
             , ("file_storage_dir", showJSON $ clusterFileStorageDir cluster)
diff --git a/test/hs/Test/Ganeti/HTools/Types.hs b/test/hs/Test/Ganeti/HTools/Types.hs
index 0cb187a21af933117f9bf5f35085adbcc32e4a01..da21725438e27ed0f7d53d352246f7eaa0ccbf05 100644
--- a/test/hs/Test/Ganeti/HTools/Types.hs
+++ b/test/hs/Test/Ganeti/HTools/Types.hs
@@ -7,7 +7,7 @@
 
 {-
 
-Copyright (C) 2009, 2010, 2011, 2012 Google Inc.
+Copyright (C) 2009, 2010, 2011, 2012, 2013 Google Inc.
 
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -46,7 +46,7 @@ import Data.List (sort)
 import Test.Ganeti.TestHelper
 import Test.Ganeti.TestCommon
 import Test.Ganeti.TestHTools
-import Test.Ganeti.Types ()
+import Test.Ganeti.Types (allDiskTemplates)
 
 import Ganeti.BasicTypes
 import qualified Ganeti.Constants as C
@@ -56,10 +56,6 @@ import qualified Ganeti.HTools.Types as Types
 
 -- * Helpers
 
--- | All disk templates (used later)
-allDiskTemplates :: [Types.DiskTemplate]
-allDiskTemplates = [minBound..maxBound]
-
 -- * Arbitrary instance
 
 $(genArbitrary ''Types.FailMode)
diff --git a/test/hs/Test/Ganeti/TestCommon.hs b/test/hs/Test/Ganeti/TestCommon.hs
index 91351b68b7396f7a56fd21b1abcbe76cf723699d..43765e5a9016809d9189d45fe87698bf6a148fe1 100644
--- a/test/hs/Test/Ganeti/TestCommon.hs
+++ b/test/hs/Test/Ganeti/TestCommon.hs
@@ -23,7 +23,43 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 
 -}
 
-module Test.Ganeti.TestCommon where
+module Test.Ganeti.TestCommon
+  ( maxMem
+  , maxDsk
+  , maxCpu
+  , maxVcpuRatio
+  , maxSpindleRatio
+  , maxNodes
+  , maxOpCodes
+  , (==?)
+  , (/=?)
+  , failTest
+  , passTest
+  , pythonCmd
+  , runPython
+  , checkPythonResult
+  , DNSChar(..)
+  , genName
+  , genFQDN
+  , genMaybe
+  , genTags
+  , genFields
+  , genUniquesList
+  , SmallRatio(..)
+  , genSetHelper
+  , genSet
+  , genIp4AddrStr
+  , genIp4Addr
+  , genIp4NetWithNetmask
+  , genIp4Net
+  , genIp6Addr
+  , genIp6Net
+  , netmask2NumHosts
+  , testSerialisation
+  , resultProp
+  , readTestData
+  , genSample
+  ) where
 
 import Control.Applicative
 import Control.Exception (catchJust)
diff --git a/test/hs/Test/Ganeti/TestHTools.hs b/test/hs/Test/Ganeti/TestHTools.hs
index 44b53c87a4bc28581a98b45ca720cec3ec6182e2..b27c34c0439ba9af268efeb40f5ca469dd5fb0fe 100644
--- a/test/hs/Test/Ganeti/TestHTools.hs
+++ b/test/hs/Test/Ganeti/TestHTools.hs
@@ -6,7 +6,7 @@
 
 {-
 
-Copyright (C) 2009, 2010, 2011, 2012 Google Inc.
+Copyright (C) 2009, 2010, 2011, 2012, 2013 Google Inc.
 
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -25,7 +25,15 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 
 -}
 
-module Test.Ganeti.TestHTools where
+module Test.Ganeti.TestHTools
+  ( nullIPolicy
+  , defGroup
+  , defGroupList
+  , defGroupAssoc
+  , createInstance
+  , makeSmallCluster
+  , setInstanceSmallerThanNode
+  ) where
 
 import qualified Data.Map as Map
 
@@ -71,14 +79,17 @@ nullIPolicy = Types.IPolicy
   , Types.iPolicySpindleRatio = maxSpindleRatio
   }
 
+-- | Default group definition.
 defGroup :: Group.Group
 defGroup = flip Group.setIdx 0 $
              Group.create "default" Types.defaultGroupID Types.AllocPreferred
                   nullIPolicy []
 
+-- | Default group, as a (singleton) 'Group.List'.
 defGroupList :: Group.List
 defGroupList = Container.fromList [(Group.idx defGroup, defGroup)]
 
+-- | Default group, as a string map.
 defGroupAssoc :: Map.Map String Types.Gdx
 defGroupAssoc = Map.singleton (Group.uuid defGroup) (Group.idx defGroup)
 
diff --git a/test/hs/Test/Ganeti/Types.hs b/test/hs/Test/Ganeti/Types.hs
index 6246e6e0cde5fb2e1ae573dce758b7cf285c51f6..3eff2f0ce9a40e9e32646c55c2ce5fca04356d40 100644
--- a/test/hs/Test/Ganeti/Types.hs
+++ b/test/hs/Test/Ganeti/Types.hs
@@ -30,13 +30,14 @@ module Test.Ganeti.Types
   ( testTypes
   , AllocPolicy(..)
   , DiskTemplate(..)
+  , allDiskTemplates
   , InstanceStatus(..)
   , NonEmpty(..)
   , Hypervisor(..)
   , JobId(..)
   ) where
 
-import Data.List (sort)
+import Data.List (delete, sort)
 import Test.QuickCheck as QuickCheck hiding (Result)
 import Test.HUnit
 import qualified Text.JSON as J
@@ -78,7 +79,22 @@ instance (Arbitrary a) => Arbitrary (Types.NonEmpty a) where
 
 $(genArbitrary ''AllocPolicy)
 
-$(genArbitrary ''DiskTemplate)
+-- | Valid disk templates (depending on configure options).
+allDiskTemplates :: [DiskTemplate]
+allDiskTemplates =
+  let all_vals = [minBound..maxBound]::[DiskTemplate]
+      sel1 = if C.enableFileStorage
+               then all_vals
+               else delete DTFile all_vals
+      sel2 = if C.enableSharedFileStorage
+               then sel1
+               else delete DTSharedFile sel1
+  in sel2
+
+-- | Custom 'Arbitrary' instance for 'DiskTemplate', which needs to
+-- handle the case of file storage being disabled at configure time.
+instance Arbitrary DiskTemplate where
+  arbitrary = elements allDiskTemplates
 
 $(genArbitrary ''InstanceStatus)
 
@@ -96,7 +112,18 @@ $(genArbitrary ''Hypervisor)
 
 $(genArbitrary ''OobCommand)
 
-$(genArbitrary ''StorageType)
+-- | Valid storage types.
+allStorageTypes :: [StorageType]
+allStorageTypes =
+  let all_vals = [minBound..maxBound]::[StorageType]
+  in if C.enableFileStorage
+       then all_vals
+       else delete StorageFile all_vals
+
+-- | Custom 'Arbitrary' instance for 'StorageType', which needs to
+-- handle the case of file storage being disabled at configure time.
+instance Arbitrary StorageType where
+  arbitrary = elements allStorageTypes
 
 $(genArbitrary ''NodeEvacMode)
 
diff --git a/test/py/ganeti.rpc_unittest.py b/test/py/ganeti.rpc_unittest.py
index ace49af8993700a262a49f00557aaf09ce68ce26..7967173b485e0480a254c3a7026f18da9c12adf1 100755
--- a/test/py/ganeti.rpc_unittest.py
+++ b/test/py/ganeti.rpc_unittest.py
@@ -1,7 +1,7 @@
 #!/usr/bin/python
 #
 
-# Copyright (C) 2010, 2011, 2012 Google Inc.
+# Copyright (C) 2010, 2011, 2012, 2013 Google Inc.
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -889,5 +889,38 @@ class TestRpcRunner(unittest.TestCase):
                     msg="Configuration objects were modified")
 
 
+class TestLegacyNodeInfo(unittest.TestCase):
+  KEY_BOOT = "bootid"
+  KEY_VG = "disk_free"
+  KEY_HV = "cpu_count"
+  VAL_BOOT = 0
+  VAL_VG = 1
+  VAL_HV = 2
+  DICT_VG = {KEY_VG: VAL_VG}
+  DICT_HV = {KEY_HV: VAL_HV}
+  STD_LST = [VAL_BOOT, [DICT_VG], [DICT_HV]]
+  STD_DICT = {
+    KEY_BOOT: VAL_BOOT,
+    KEY_VG: VAL_VG,
+    KEY_HV: VAL_HV
+    }
+
+  def testStandard(self):
+    result = rpc.MakeLegacyNodeInfo(self.STD_LST)
+    self.assertEqual(result, self.STD_DICT)
+
+  def testReqVg(self):
+    my_lst = [self.VAL_BOOT, [], [self.DICT_HV]]
+    self.assertRaises(ValueError, rpc.MakeLegacyNodeInfo, my_lst)
+
+  def testNoReqVg(self):
+    my_lst = [self.VAL_BOOT, [], [self.DICT_HV]]
+    result = rpc.MakeLegacyNodeInfo(my_lst, require_vg_info = False)
+    self.assertEqual(result, {self.KEY_BOOT: self.VAL_BOOT,
+                              self.KEY_HV: self.VAL_HV})
+    result = rpc.MakeLegacyNodeInfo(self.STD_LST, require_vg_info = False)
+    self.assertEqual(result, self.STD_DICT)
+
+
 if __name__ == "__main__":
   testutils.GanetiTestProgram()