diff --git a/INSTALL b/INSTALL index d65af860507e52f5d02e9d860bb4fcb7f15934dd..e8957a0f6ebeed8fc65a2e16bfef4b26e61872c1 100644 --- a/INSTALL +++ b/INSTALL @@ -56,9 +56,19 @@ packages, except for RBD, DRBD and Xen:: $ apt-get install lvm2 ssh bridge-utils iproute iputils-arping \ ndisc6 python python-pyopenssl openssl \ - python-pyparsing python-simplejson \ - python-pyinotify python-pycurl socat fping \ - python-ipaddr python-bitarray + python-pyparsing python-simplejson python-bitarray \ + python-pyinotify python-pycurl python-ipaddr socat fping + +If bitarray is missing it can be installed from easy-install:: + + $ easy_install bitarray + +Or on newer distributions (eg. Debian Wheezy) the above becomes:: + + $ apt-get install lvm2 ssh bridge-utils iproute iputils-arping \ + ndisc6 python python-openssl openssl \ + python-pyparsing python-simplejson python-bitarray \ + python-pyinotify python-pycurl python-ipaddr socat fping Note that this does not install optional packages:: @@ -194,7 +204,7 @@ can use either apt:: or ``cabal``, after installing a required non-Haskell dependency:: $ apt-get install libpcre3-dev - $ cabal install hslogger Crypto text hinotify regex-pcre \ + $ cabal install hslogger Crypto text hinotify==0.3.2 regex-pcre \ attoparsec vector snap-server to install them. diff --git a/Makefile.am b/Makefile.am index dcc85c821220c464eb0c038387ee499eb0e14219..d7cc0b7029dffd4411732e62f3fa51253a103cd1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -444,7 +444,11 @@ mandocrst = $(addprefix doc/man-,$(notdir $(manrst))) HS_BIN_PROGS=src/htools # Haskell programs to be installed in the MYEXECLIB dir +if ENABLE_MOND HS_MYEXECLIB_PROGS=src/mon-collector +else +HS_MYEXECLIB_PROGS= +endif # Haskell programs to be compiled by "make really-all" HS_COMPILE_PROGS= \ diff --git a/NEWS b/NEWS index c67734868d936f00cc6f8767ea3d929e45383fa0..6fad866555578ab8eefcfd0e9cde39adb79cf199 100644 --- a/NEWS +++ b/NEWS @@ -24,10 +24,10 @@ Version 2.8.0 beta1 configuration back to the previous stable version. -Version 2.7.0 beta3 -------------------- +Version 2.7.0 rc1 +----------------- -*(Released Mon, 22 Apr 2013)* +*(unreleased)* Incompatible/important changes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -87,7 +87,8 @@ New features ``plain`` disk template are supported. - The KVM hypervisor has been updated with many new hypervisor parameters, including a generic one for passing arbitrary command line - values. See a complete list in :manpage:`gnt-instance(8)`. + values. See a complete list in :manpage:`gnt-instance(8)`. It is now + compatible up to qemu 1.4. - A new tool, called ``mon-collector``, is the stand-alone executor of the data collectors for a monitoring system. As of this version, it just includes the DRBD data collector, that can be executed by calling @@ -131,7 +132,31 @@ Misc changes - The functionality for allocating multiple instances at once has been overhauled and is now also available through :doc:`RAPI <rapi>`. -Since beta2: + +Since beta3: + +- Fix kvm compatibility with qemu 1.4 (Issue 389) +- Documentation updates (admin guide, upgrade notes, install + instructions) (Issue 372) +- Fix gnt-group list nodes and instances count (Issue 436) +- Fix compilation without non-mandatory libraries (Issue 441) +- Fix xen-hvm hypervisor forcing nics to type 'ioemu' (Issue 247) +- Make confd logging more verbose at INFO level (Issue 435) +- Improve "networks" documentation in :manpage:`gnt-instance(8)` +- Fix failure path for instance storage type conversion (Issue 229) +- Update htools text backend documentation +- Improve the renew-crypto section of :manpage:`gnt-cluster(8)` +- Disable inter-cluster instance move for file-based instances, because + it is dependant on instance export, which is not supported for + file-based instances. (Issue 414) + + +Version 2.7.0 beta3 +------------------- + +*(Released Mon, 22 Apr 2013)* + +This was the third beta release of the 2.7 series. Since beta2: - Fix hail to verify disk instance policies on a per-disk basis (Issue 418). - Fix data loss on wrong usage of ``gnt-instance move`` diff --git a/UPGRADE b/UPGRADE index c3b3de51a9ece1b3895b25fbbcaa9569b2b37817..a769b92e344c739737c308296e5ef36e1fe098ec 100644 --- a/UPGRADE +++ b/UPGRADE @@ -63,6 +63,12 @@ To run commands on all nodes, the `distributed shell (dsh) $ gnt-cluster redist-conf +#. If you use file storage, check that the ``/etc/ganeti/file-storage-paths`` +#. is correct on all nodes. For security reasons it's not copied +#. automatically, but it can be copied manually via:: + + $ gnt-cluster copyfile /etc/ganeti/file-storage-paths + #. Restart daemons again on all nodes:: $ /etc/init.d/ganeti restart diff --git a/configure.ac b/configure.ac index 2d2439f72c991345882c2a6554da3c1089d43a2b..1d01b30657ecb70a9696e4486594e00aa1fbfd96 100644 --- a/configure.ac +++ b/configure.ac @@ -566,8 +566,13 @@ if test "$enable_monitoring" != no; then [MONITORING_PKG="$MONITORING_PKG attoparsec"]) AC_GHC_PKG_CHECK([snap-server], [], [MONITORING_PKG="$MONITORING_PKG snap-server"]) + MONITORING_DEP= + if test "$has_confd" = False; then + MONITORING_DEP="$MONITORING_DEP confd" + fi + has_monitoring_pkg=False if test -z "$MONITORING_PKG"; then - has_monitoring=True + has_monitoring_pkg=True elif test "$enable_monitoring" = check; then AC_MSG_WARN(m4_normalize([The required extra libraries for the monitoring daemon were not found ($MONITORING_PKG), @@ -577,9 +582,23 @@ if test "$enable_monitoring" != no; then required libraries were not found: $MONITORING_PKG])) fi + has_monitoring_dep=False + if test -z "$MONITORING_DEP"; then + has_monitoring_dep=True + elif test "$enable_monitoring" = check; then + AC_MSG_WARN(m4_normalize([The optional Ganeti components required for the + monitoring agent were not enabled + ($MONITORING_DEP), monitoring disabled])) + else + AC_MSG_FAILURE(m4_normalize([The monitoring functionality was requested, but + required optional Ganeti components were not + found: $MONITORING_DEP])) + fi + fi -if test "$has_monitoring" = True; then - AC_MSG_NOTICE([Enabling the monitoring daemon usage]) +if test "$has_monitoring_pkg" = True -a "$has_monitoring_dep" = True; then + has_monitoring=True + AC_MSG_NOTICE([Enabling the monitoring agent usage]) fi AC_SUBST(ENABLE_MOND, $has_monitoring) AM_CONDITIONAL([ENABLE_MOND], [test "$has_monitoring" = True]) diff --git a/doc/admin.rst b/doc/admin.rst index 6a0ad585bc78ea3ec68281b72d2b060466e78dc0..634fb1711ecc356d5e8952e31463e8671d7713a6 100644 --- a/doc/admin.rst +++ b/doc/admin.rst @@ -124,7 +124,18 @@ diskless file The instance will use plain files as backend for its disks. No redundancy is provided, and this is somewhat more difficult to - configure for high performance. + configure for high performance. Note that for security reasons the + file storage directory must be listed under + ``/etc/ganeti/file-storage-paths``, and that file is not copied + automatically to all nodes by Ganeti. + +sharedfile + The instance will use plain files as backend, but Ganeti assumes that + those files will be available and in sync automatically on all nodes. + This allows live migration and failover of instances using this + method. As for ``file`` the file storage directory must be listed under + ``/etc/ganeti/file-storage-paths`` or ganeti will refuse to create + instances under it. plain The instance will use LVM devices as backend for its disks. No @@ -146,6 +157,11 @@ rbd The instance will use Volumes inside a RADOS cluster as backend for its disks. It will access them using the RADOS block device (RBD). +ext + The instance will use an external storage provider. See + :manpage:`ganeti-extstorage-interface(7)` for how to implement one. + + IAllocator ~~~~~~~~~~ @@ -263,7 +279,7 @@ can give include, among others: instance is created. The IP and/or bridge of the NIC can be changed via ``--net 0:ip=IP,link=BRIDGE`` -See the manpage for gnt-instance for the detailed option list. +See :manpage:`ganeti-instance(8)` for the detailed option list. For example if you want to create an highly available instance, with a single disk of 50GB and the default memory size, having primary node diff --git a/doc/install.rst b/doc/install.rst index a9e6ce30d1142e2ed4845bad564165d86cbd3d02..c50f1b26e76aaa08b4667b60ed5022ab51ce74bd 100644 --- a/doc/install.rst +++ b/doc/install.rst @@ -301,6 +301,13 @@ instances on a node. $ apt-get install drbd8-source drbd8-utils $ m-a update $ m-a a-i drbd8 + + Or on newer versions, if the kernel already has modules: + + $ apt-get install drbd8-utils + + Then to configure it for Ganeti:: + $ echo drbd minor_count=128 usermode_helper=/bin/true >> /etc/modules $ depmod -a $ modprobe drbd minor_count=128 usermode_helper=/bin/true diff --git a/lib/bdev.py b/lib/bdev.py index 0f9339e1d066e09ae5d7d9c2d5a3c2de62b0ec6d..b1e1ef9e0a0f500b5535dfd34f3bbe739eff58fc 100644 --- a/lib/bdev.py +++ b/lib/bdev.py @@ -184,7 +184,9 @@ def _CheckFileStoragePath(path, allowed): break else: raise errors.FileStoragePathError("Path '%s' is not acceptable for file" - " storage" % path) + " storage. A possible fix might be to add" + " it to /etc/ganeti/file-storage-paths" + " on all nodes." % path) def _LoadAllowedFileStoragePaths(filename): diff --git a/lib/cmdlib.py b/lib/cmdlib.py index 2c6eccb537a97c039d268324461c291c58f9685f..64b8c47b890d6b0cab7e790d6149f15959ec021b 100644 --- a/lib/cmdlib.py +++ b/lib/cmdlib.py @@ -4227,10 +4227,14 @@ class LUClusterSetParams(LogicalUnit): node_list = self.owned_locks(locking.LEVEL_NODE) + vm_capable_nodes = [node.name + for node in self.cfg.GetAllNodesInfo().values() + if node.name in node_list and node.vm_capable] + # if vg_name not None, checks given volume group on all nodes if self.op.vg_name: - vglist = self.rpc.call_vg_list(node_list) - for node in node_list: + vglist = self.rpc.call_vg_list(vm_capable_nodes) + for node in vm_capable_nodes: msg = vglist[node].fail_msg if msg: # ignoring down node @@ -14152,11 +14156,22 @@ class LUInstanceSetParams(LogicalUnit): feedback_fn("Initializing DRBD devices...") # all child devices are in place, we can now create the DRBD devices - for disk in anno_disks: - for (node, excl_stor) in [(pnode, p_excl_stor), (snode, s_excl_stor)]: - f_create = node == pnode - _CreateSingleBlockDev(self, node, instance, disk, info, f_create, - excl_stor) + try: + for disk in anno_disks: + for (node, excl_stor) in [(pnode, p_excl_stor), (snode, s_excl_stor)]: + f_create = node == pnode + _CreateSingleBlockDev(self, node, instance, disk, info, f_create, + excl_stor) + except errors.GenericError, e: + feedback_fn("Initializing of DRBD devices failed;" + " renaming back original volumes...") + for disk in new_disks: + self.cfg.SetDiskID(disk, pnode) + rename_back_list = [(n.children[0], o.logical_id) + for (n, o) in zip(new_disks, instance.disks)] + result = self.rpc.call_blockdev_rename(pnode, rename_back_list) + result.Raise("Failed to rename LVs back after error %s" % str(e)) + raise # at this point, the instance has been modified instance.disk_template = constants.DT_DRBD8 @@ -17094,7 +17109,8 @@ def _CheckForConflictingIp(lu, ip, node): """ (conf_net, _) = lu.cfg.CheckIPInNodeGroup(ip, node) if conf_net is not None: - raise errors.OpPrereqError(("Conflicting IP address found: '%s' != '%s'" % + raise errors.OpPrereqError(("The requested IP address (%s) belongs to" + " network %s, but the target NIC does not." % (ip, conf_net)), errors.ECODE_STATE) diff --git a/lib/constants.py b/lib/constants.py index 0e49de6340cbd17c97c8e98a123b0a116cbc28fa..59663a0906102b17a1af3ff59c8607c07af07ec9 100644 --- a/lib/constants.py +++ b/lib/constants.py @@ -942,6 +942,7 @@ HV_VGA = "vga" HV_KVM_EXTRA = "kvm_extra" HV_KVM_MACHINE_VERSION = "machine_version" HV_KVM_PATH = "kvm_path" +HV_VIF_TYPE = "vif_type" HVS_PARAMETER_TYPES = { @@ -1011,6 +1012,7 @@ HVS_PARAMETER_TYPES = { HV_VGA: VTYPE_STRING, HV_KVM_EXTRA: VTYPE_STRING, HV_KVM_MACHINE_VERSION: VTYPE_STRING, + HV_VIF_TYPE: VTYPE_STRING, } HVS_PARAMETERS = frozenset(HVS_PARAMETER_TYPES.keys()) @@ -1407,6 +1409,15 @@ HT_KVM_VALID_NIC_TYPES = compat.UniqueFrozenset([ HT_NIC_PARAVIRTUAL, ]) +# Vif types +# default vif type in xen-hvm +HT_HVM_VIF_IOEMU = "ioemu" +HT_HVM_VIF_VIF = "vif" +HT_HVM_VALID_VIF_TYPES = compat.UniqueFrozenset([ + HT_HVM_VIF_IOEMU, + HT_HVM_VIF_VIF, + ]) + # Disk types HT_DISK_IOEMU = "ioemu" HT_DISK_IDE = "ide" @@ -2027,6 +2038,7 @@ HVC_DEFAULTS = { HV_CPU_MASK: CPU_PINNING_ALL, HV_CPU_CAP: 0, HV_CPU_WEIGHT: 256, + HV_VIF_TYPE: HT_HVM_VIF_IOEMU, }, HT_KVM: { HV_KVM_PATH: KVM_PATH, diff --git a/lib/hypervisor/hv_kvm.py b/lib/hypervisor/hv_kvm.py index 25e43b7399bd212c6bea4919ef173719656c4098..9cff48fdf50d218bd15567f6e46edd0499750368 100644 --- a/lib/hypervisor/hv_kvm.py +++ b/lib/hypervisor/hv_kvm.py @@ -564,6 +564,8 @@ class KVMHypervisor(hv_base.BaseHypervisor): _ENABLE_KVM_RE = re.compile(r"^-enable-kvm\s", re.M) _DISABLE_KVM_RE = re.compile(r"^-disable-kvm\s", re.M) _NETDEV_RE = re.compile(r"^-netdev\s", re.M) + _DISPLAY_RE = re.compile(r"^-display\s", re.M) + _MACHINE_RE = re.compile(r"^-machine\s", re.M) _NEW_VIRTIO_RE = re.compile(r"^name \"%s\"" % _VIRTIO_NET_PCI, re.M) # match -drive.*boot=on|off on different lines, but in between accept only # dashes not preceeded by a new line (which would mean another option @@ -1034,6 +1036,7 @@ class KVMHypervisor(hv_base.BaseHypervisor): """ # pylint: disable=R0912,R0914,R0915 hvp = instance.hvparams + self.ValidateParameters(hvp) pidfile = self._InstancePidFile(instance.name) kvm = hvp[constants.HV_KVM_PATH] @@ -1064,7 +1067,24 @@ class KVMHypervisor(hv_base.BaseHypervisor): mversion = hvp[constants.HV_KVM_MACHINE_VERSION] if not mversion: mversion = self._GetDefaultMachineVersion(kvm) - kvm_cmd.extend(["-M", mversion]) + if self._MACHINE_RE.search(kvmhelp): + # TODO (2.8): kernel_irqchip and kvm_shadow_mem machine properties, as + # extra hypervisor parameters. We should also investigate whether and how + # shadow_mem should be considered for the resource model. + if (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED): + specprop = ",accel=kvm" + else: + specprop = "" + machinespec = "%s%s" % (mversion, specprop) + kvm_cmd.extend(["-machine", machinespec]) + else: + kvm_cmd.extend(["-M", mversion]) + if (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED and + self._ENABLE_KVM_RE.search(kvmhelp)): + kvm_cmd.extend(["-enable-kvm"]) + elif (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED and + self._DISABLE_KVM_RE.search(kvmhelp)): + kvm_cmd.extend(["-disable-kvm"]) kernel_path = hvp[constants.HV_KERNEL_PATH] if kernel_path: @@ -1075,18 +1095,9 @@ class KVMHypervisor(hv_base.BaseHypervisor): boot_floppy = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_FLOPPY boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK - self.ValidateParameters(hvp) - if startup_paused: kvm_cmd.extend([_KVM_START_PAUSED_FLAG]) - if (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED and - self._ENABLE_KVM_RE.search(kvmhelp)): - kvm_cmd.extend(["-enable-kvm"]) - elif (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED and - self._DISABLE_KVM_RE.search(kvmhelp)): - kvm_cmd.extend(["-disable-kvm"]) - if boot_network: kvm_cmd.extend(["-boot", "n"]) @@ -1342,7 +1353,12 @@ class KVMHypervisor(hv_base.BaseHypervisor): kvm_cmd.extend(["-spice", spice_arg]) else: - kvm_cmd.extend(["-nographic"]) + # From qemu 1.4 -nographic is incompatible with -daemonize. The new way + # also works in earlier versions though (tested with 1.1 and 1.3) + if self._DISPLAY_RE.search(kvmhelp): + kvm_cmd.extend(["-display", "none"]) + else: + kvm_cmd.extend(["-nographic"]) if hvp[constants.HV_USE_LOCALTIME]: kvm_cmd.extend(["-localtime"]) diff --git a/lib/hypervisor/hv_xen.py b/lib/hypervisor/hv_xen.py index 8789571bc35e8d33189f5247846f4b96954ad92a..1d0b5872decb2f467ce988b434e35eddaebaeddb 100644 --- a/lib/hypervisor/hv_xen.py +++ b/lib/hypervisor/hv_xen.py @@ -909,6 +909,8 @@ class XenHvmHypervisor(XenHypervisor): constants.HV_CPU_CAP: hv_base.NO_CHECK, constants.HV_CPU_WEIGHT: (False, lambda x: 0 < x < 65535, "invalid weight", None, None), + constants.HV_VIF_TYPE: + hv_base.ParamInSet(False, constants.HT_HVM_VALID_VIF_TYPES), } def _GetConfig(self, instance, startup_memory, block_devices): @@ -980,14 +982,23 @@ class XenHvmHypervisor(XenHypervisor): config.write("localtime = 1\n") vif_data = [] + # Note: what is called 'nic_type' here, is used as value for the xen nic + # vif config parameter 'model'. For the xen nic vif parameter 'type', we use + # the 'vif_type' to avoid a clash of notation. nic_type = hvp[constants.HV_NIC_TYPE] + if nic_type is None: + vif_type_str = "" + if hvp[constants.HV_VIF_TYPE]: + vif_type_str = ", type=%s" % hvp[constants.HV_VIF_TYPE] # ensure old instances don't change - nic_type_str = ", type=ioemu" + nic_type_str = vif_type_str elif nic_type == constants.HT_NIC_PARAVIRTUAL: nic_type_str = ", type=paravirtualized" else: - nic_type_str = ", model=%s, type=ioemu" % nic_type + # parameter 'model' is only valid with type 'ioemu' + nic_type_str = ", model=%s, type=%s" % \ + (nic_type, constants.HT_HVM_VIF_IOEMU) for nic in instance.nics: nic_str = "mac=%s%s" % (nic.mac, nic_type_str) ip = getattr(nic, "ip", None) diff --git a/man/gnt-instance.rst b/man/gnt-instance.rst index 0a9becd175cdb69ce29d1b777cb6243cb10b14f6..2fb2cc84206a10cf59240ae18693228d3a06567d 100644 --- a/man/gnt-instance.rst +++ b/man/gnt-instance.rst @@ -31,7 +31,8 @@ ADD | {\--disk=*N*: {size=*VAL* \| adopt=*LV*}[,options...] | \| {size=*VAL*,provider=*PROVIDER*}[,param=*value*... ][,options...] | \| {-s|\--os-size} *SIZE*} -| [\--no-ip-check] [\--no-name-check] [\--no-start] [\--no-install] +| [\--no-ip-check] [\--no-name-check] [\--no-conflicts-check] +| [\--no-start] [\--no-install] | [\--net=*N* [:options...] \| \--no-nics] | [{-B|\--backend-parameters} *BEPARAMS*] | [{-H|\--hypervisor-parameters} *HYPERVISOR* [: option=*value*... ]] @@ -124,7 +125,13 @@ mac ip specifies the IP address assigned to the instance from the Ganeti side (this is not necessarily what the instance will use, but what - the node expects the instance to use) + the node expects the instance to use). Note that if an IP in the + range of a network configured with **gnt-network**\(8) is used, + and the NIC is not already connected to it, this network has to be + passed in the **network** parameter if this NIC is meant to be + connected to the said network. ``--no-conflicts-check`` can be used + to override this check. The special value **pool** causes Ganeti to + select an IP from the the network the NIC is or will be connected to. mode specifies the connection mode for this NIC: routed, bridged or @@ -278,6 +285,17 @@ nic\_type - e1000 (KVM) - paravirtual (default for KVM) (HVM & KVM) +vif\_type + Valid for the Xen HVM hypervisor. + + This parameter specifies the vif type of the nic configuration + of the instance. Unsetting the value leads to no type being specified + in the configuration. Note that this parameter only takes effect when + the 'nic_type' is not set. The possible options are: + + - ioemu + - vif + disk\_type Valid for the Xen HVM and KVM hypervisors. diff --git a/src/Ganeti/Query/Server.hs b/src/Ganeti/Query/Server.hs index dc6d0cfb4ba83827f63c17bac9e41040b662dd3f..3c8d8a9d93731f2d21fa99b8daa6fdac7b768872 100644 --- a/src/Ganeti/Query/Server.hs +++ b/src/Ganeti/Query/Server.hs @@ -177,7 +177,6 @@ handleCall _ op = return . Bad $ GenericError ("Luxi call '" ++ strOfOp op ++ "' not implemented") - -- | Given a decoded luxi request, executes it and sends the luxi -- response back to the client. handleClientMsg :: Client -> ConfigReader -> LuxiOp -> IO Bool @@ -188,11 +187,13 @@ handleClientMsg client creader args = do (!status, !rval) <- case call_result of Bad err -> do - logWarning $ "Failed to execute request: " ++ show err + logWarning $ "Failed to execute request " ++ show args ++ ": " + ++ show err return (False, showJSON err) Ok result -> do -- only log the first 2,000 chars of the result logDebug $ "Result (truncated): " ++ take 2000 (J.encode result) + logInfo $ "Successfully handled " ++ strOfOp args return (True, result) sendMsg client $ buildResponse status rval return True diff --git a/src/Ganeti/THH.hs b/src/Ganeti/THH.hs index 4f8daedf9414f9528d0779157b5dd7ee0ccd8283..aa14642c663291666699dc37ba6105a4ceaa37e4 100644 --- a/src/Ganeti/THH.hs +++ b/src/Ganeti/THH.hs @@ -660,7 +660,7 @@ genStrOfKey = genConstrToStr ensureLower -- | Generates the LuxiOp data type. -- -- This takes a Luxi operation definition and builds both the --- datatype and the function trnasforming the arguments to JSON. +-- datatype and the function transforming the arguments to JSON. -- We can't use anything less generic, because the way different -- operations are serialized differs on both parameter- and top-level. -- diff --git a/tools/move-instance b/tools/move-instance index 6e340b8a23412678eb2ea9133dd336bee1c38863..b75e4123df97ffca28c43e720a4fcb7f3d674c79 100755 --- a/tools/move-instance +++ b/tools/move-instance @@ -563,6 +563,9 @@ class MoveSourceExecutor(object): logging.info("Retrieving instance information from source cluster") instinfo = self._GetInstanceInfo(src_client, mrt.PollJob, mrt.move.src_instance_name) + if instinfo["disk_template"] == constants.DT_FILE: + raise Error("Inter-cluster move of file-based instances is not" + " supported.") logging.info("Preparing export on source cluster") expinfo = self._PrepareExport(src_client, mrt.PollJob,