commit 94d312e52635bed7daac9e86a77873eb2e114e5e
Merge: 2d2f44d d54e169
Author: Petr Pudlak <[email protected]>
Date: Fri Mar 20 15:29:33 2015 +0100
Merge branch 'stable-2.12' into stable-2.13
* stable-2.12
QA: Fix CheckFileUnmodified to work with vcluster
QA: Fix white-spaces in CheckFileUnmodified
QA: Check that the cluster verify doesn't change the config
QA: Allow to check that an operation doesn't change a file
Use only shared configuration lock for ComputeDRBDMap
Only assert properties of non-None objects
If any IO error happens during job forking, retry
Add a function for retrying `MonadError` computations
* stable-2.11
Improve speed of Xen hypervisor unit tests
Improve Xen instance state handling
* stable-2.10
Make QA fail if KVM hotplugging fails
Always preserve QA command output
Don't lose stdout/stderr in AssertCommand
qa_utils: Allow passing fail=None to AssertCommand
qa_utils: Make AssertCommand return stdout/stderr as well
Allow plain/DRBD conversions regardless of lack of disks
Add support for ipolicy modifications to mock config
Conflicts:
lib/cmdlib/instance.py - propagate changes from 2.12
qa/qa_cluster.py - propagate changes from 2.12
qa/qa_instance.py - propagate changes from 2.12
qa/qa_utils.py - propagate changes from 2.12
src/Ganeti/Utils/Monad.hs - merge changes from both branches
diff --cc lib/cmdlib/instance.py
index bed2e3d,d5b2c6b..269e47f
--- a/lib/cmdlib/instance.py
+++ b/lib/cmdlib/instance.py
@@@ -3916,11 -3679,18 +3916,16 @@@ class LUInstanceSetParams(LogicalUnit)
"""
secondary_nodes = self.cfg.GetInstanceSecondaryNodes(self.instance.uuid)
- assert len(secondary_nodes) == 1
+
assert self.instance.disk_template == constants.DT_DRBD8
+ assert len(secondary_nodes) == 1 or not self.instance.disks
+
- pnode_uuid = self.instance.primary_node
-
+ # it will not be possible to calculate the snode_uuid later
+ snode_uuid = None
+ if secondary_nodes:
+ snode_uuid = secondary_nodes[0]
- snode_uuid = secondary_nodes[0]
- feedback_fn("Converting template to plain")
+ feedback_fn("Converting disk template from 'drbd' to 'plain'")
disks = self.cfg.GetInstanceDisks(self.instance.uuid)
old_disks = AnnotateDiskParams(self.instance, disks, self.cfg)
diff --cc qa/qa_cluster.py
index 51a3b15,adf508a..166a5ee
--- a/qa/qa_cluster.py
+++ b/qa/qa_cluster.py
@@@ -51,8 -51,8 +51,8 @@@ import qa_job_util
import qa_logging
import qa_utils
-from qa_utils import AssertEqual, AssertCommand, GetCommandOutput, \
- CheckFileUnmodified
+from qa_utils import AssertEqual, AssertCommand, AssertRedirectedCommand, \
- GetCommandOutput
++ GetCommandOutput, CheckFileUnmodified
# Prefix for LVM volumes created by QA code during tests
diff --cc qa/qa_instance.py
index ffe6a31,6cbe96e..a00cfa7
--- a/qa/qa_instance.py
+++ b/qa/qa_instance.py
@@@ -633,22 -630,7 +652,14 @@@ def TestInstanceModify(instance)
])
elif default_hv == constants.HT_KVM and \
qa_config.TestEnabled("instance-device-hotplug"):
- args.extend([
- ["--net", "-1:add", "--hotplug"],
- ["--net", "-1:modify,mac=aa:bb:cc:dd:ee:ff", "--hotplug", "--force"],
- ["--net", "-1:remove", "--hotplug"],
- ])
- args.extend([
- ["--disk", "-1:add,size=1G", "--hotplug"],
- ["--disk", "-1:remove", "--hotplug"],
- ])
+ _TestKVMHotplug(instance)
+ elif default_hv == constants.HT_LXC:
+ args.extend([
+ ["-H", "%s=0" % constants.HV_CPU_MASK],
+ ["-H", "%s=%s" % (constants.HV_CPU_MASK, constants.VALUE_DEFAULT)],
+ ["-H", "%s=0" % constants.HV_LXC_NUM_TTYS],
+ ["-H", "%s=%s" % (constants.HV_LXC_NUM_TTYS, constants.VALUE_DEFAULT)],
+ ])
url = "http://example.com/busybox.img"
args.extend([
diff --cc src/Ganeti/Utils/Monad.hs
index c1bd630,0000000..cd09a0d
mode 100644,000000..100644
--- a/src/Ganeti/Utils/Monad.hs
+++ b/src/Ganeti/Utils/Monad.hs
@@@ -1,87 -1,0 +1,97 @@@
+{-| Utility functions for MonadPlus operations
+
+-}
+
+{-
+
+Copyright (C) 2014 Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+1. Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+-}
+
+module Ganeti.Utils.Monad
+ ( mretryN
+ , retryMaybeN
+ , anyM
+ , allM
+ , orM
+ , unfoldrM
+ , unfoldrM'
++ , retryErrorN
+ ) where
+
+import Control.Monad
++import Control.Monad.Error
+import Control.Monad.Trans.Maybe
+
+-- | Retries the given action up to @n@ times.
+-- The action signals failure by 'mzero'.
+mretryN :: (MonadPlus m) => Int -> (Int -> m a) -> m a
+mretryN n = msum . flip map [1..n]
+
+-- | Retries the given action up to @n@ times.
+-- The action signals failure by 'mzero'.
+retryMaybeN :: (Monad m) => Int -> (Int -> MaybeT m a) -> m (Maybe a)
+retryMaybeN = (runMaybeT .) . mretryN
+
++-- | Retries the given action up to @n@ times until it succeeds.
++-- If all actions fail, the error of the last one is returned.
++-- The action is always run at least once, even if @n@ is less than 1.
++retryErrorN :: (MonadError e m) => Int -> (Int -> m a) -> m a
++retryErrorN n f = loop 1
++ where
++ loop i | i < n = catchError (f i) (const $ loop (i + 1))
++ | otherwise = f i
+
- -- From monad-loops (until we can / want to depend on it):
++-- * From monad-loops (until we can / want to depend on it):
+
+-- | Short-circuit 'any' with a monadic predicate.
+anyM :: (Monad m) => (a -> m Bool) -> [a] -> m Bool
+anyM p = foldM (\v x -> if v then return True else p x) False
+
+-- | Short-circuit 'all' with a monadic predicate.
+allM :: (Monad m) => (a -> m Bool) -> [a] -> m Bool
+allM p = foldM (\v x -> if v then p x else return False) True
+
+-- | Short-circuit 'or' for values of type Monad m => m Bool
+orM :: (Monad m) => [m Bool] -> m Bool
+orM = anyM id
+
+-- |See 'Data.List.unfoldr'. This is a monad-friendly version of that.
+unfoldrM :: (Monad m) => (a -> m (Maybe (b,a))) -> a -> m [b]
+unfoldrM = unfoldrM'
+
+-- | See 'Data.List.unfoldr'. This is a monad-friendly version of that, with a
+-- twist. Rather than returning a list, it returns any MonadPlus type of your
+-- choice.
+unfoldrM' :: (Monad m, MonadPlus f) => (a -> m (Maybe (b,a))) -> a -> m (f b)
+unfoldrM' f z = do
+ x <- f z
+ case x of
+ Nothing -> return mzero
+ Just (x', z') -> do
+ xs <- unfoldrM' f z'
+ return (return x' `mplus` xs)