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)

Reply via email to