On Thu, Nov 06, 2014 at 12:30:46AM +0200, Alex Pyrgiotis wrote:
> The attach/detach test is merged with the add/remove test. The test is
> the following: an extra disk is added in each instance and is given a
> random name. Once it has been added successfully, we detach it and check
> if the instance operates normally. Then, we pick instances randomly and
> attach a detached disk to them, using an algorithm that matches a disk
> to an instance based on its disk nodes. Then, we remove the disk.
>
> Also, move the start/stop at the top of the tests, since the start/stop
> functionality is erroneously used first by other functions (e.g. rename)
> and then tested on its own.
>
> Signed-off-by: Alex Pyrgiotis <[email protected]>
>
> diff --git a/lib/tools/burnin.py b/lib/tools/burnin.py
> index 63f5a4c..82be852 100755
> --- a/lib/tools/burnin.py
> +++ b/lib/tools/burnin.py
> @@ -37,6 +37,8 @@ import optparse
>  import time
>  import socket
>  import urllib
> +import random
> +import string # pylint: disable=W0402
>  from itertools import izip, islice, cycle
>  from cStringIO import StringIO
>
> @@ -130,6 +132,10 @@ def Err(msg, exit_code=1):
>    sys.exit(exit_code)
>
>
> +def RandomString(size=8, chars=string.ascii_uppercase + string.digits):
> +  return ''.join(random.choice(chars) for x in range(size))
> +
> +
>  class SimpleOpener(urllib.FancyURLopener):
>    """A simple url opener"""
>    # pylint: disable=W0221
> @@ -329,6 +335,7 @@ class Burner(object):
>      self.hvp = self.bep = None
>      self.ParseOptions()
>      self.cl = cli.GetClient()
> +    self.disk_nodes = {}

Ding-ding-ding! I'm <del>terribly sorry</del><ins>happy</ins> to inform
you that you hit the "max-attributes" limit of 20. Can you please split
Burnin in a meaningful way or remove some attributes (not necessarily
yours)? If you get it below 20, can you also lower the max-attributes to
the new highest number (19)?

>      self.GetState()
>
>    def ClearFeedbackBuf(self):
> @@ -597,6 +604,16 @@ class Burner(object):
>      self.hv_can_migrate = \
>        hypervisor.GetHypervisorClass(self.hypervisor).CAN_MIGRATE
>
> +  def FindMatchingDisk(self, instance, disks):
> +    """Find a disk whose nodes match the instance's disk nodes."""
> +    instance_nodes = self.disk_nodes[instance]
> +    for disk, disk_nodes in disks.iteritems():
> +      if set(instance_nodes) == set(disk_nodes):
> +        # Erase that disk from the dictionary so that we don't pick it
again.
> +        del disks[disk]
> +        return disk
> +    Err("Couldn't find matching detached disk for instance %s" %
instance)
> +
>    @_DoCheckInstances
>    @_DoBatch(False)
>    def BurnCreateInstances(self):
> @@ -622,6 +639,15 @@ class Burner(object):
>
>        Log(msg, indent=2)
>
> +      # Calculate the disk nodes for the instance based on the disk
template.
> +      if self.opts.disk_template in constants.DTS_EXT_MIRROR:
> +        nodes = []
> +      elif self.opts.disk_template in constants.DTS_INT_MIRROR:
> +        nodes = [pnode, snode]
> +      else:
> +        nodes = [pnode]
> +      self.disk_nodes[instance] = nodes
> +
>        op = opcodes.OpInstanceCreate(instance_name=instance,
>                                      disks=[{"size": size}
>                                             for size in self.disk_size],
> @@ -646,6 +672,64 @@ class Burner(object):
>        remove_instance = lambda name: lambda: self.to_rem.append(name)
>        self.ExecOrQueue(instance, [op], post_process=remove_instance(
instance))
>
> +  @_DoCheckInstances
> +  @_DoBatch(False)
> +  def BurnAddDisks(self):
> +    """Add an extra disk to every instance and then detach it."""
> +    Log("Adding and detaching disks")
> +
> +    # Temporary dict that keeps the generated disk names and their nodes.
> +    self._disks = {}
> +
> +    # Iterate all instances, start them, add a disk with a unique name
and
> +    # detach it. Do all disk operations with hotplugging (if possible).
> +    for instance in self.instances:
> +      Log("instance %s", instance, indent=1)
> +      disk_name = RandomString()
> +      self._disks[disk_name] = self.disk_nodes[instance]
> +      op_stop = self.StopInstanceOp(instance)
> +      op_add = opcodes.OpInstanceSetParams(
> +        instance_name=instance, hotplug_if_possible=True,
> +        disks=[(constants.DDM_ADD, {"size": self.disk_size[0],
> +                                    "name": disk_name})])
> +      op_detach = opcodes.OpInstanceSetParams(
> +        instance_name=instance, hotplug_if_possible=True,
> +        disks=[(constants.DDM_DETACH, {})])
> +      op_start = self.StartInstanceOp(instance)
> +      Log("adding a disk with name %s" % disk_name, indent=2)
> +      Log("detaching last disk", indent=2)
> +      self.ExecOrQueue(instance, [op_start, op_add, op_detach, op_stop,
> +                                  op_start])
> +
> +  @_DoCheckInstances
> +  @_DoBatch(False)
> +  def BurnRemoveDisks(self):
> +    """Attach a previously detached disk to an instance and then remove
it."""
> +    Log("Attaching and removing disks")
> +
> +    # Iterate all instances in random order, attach the detached disks,
remove
> +    # them and then restart the instances. Do all disk operation with
> +    # hotplugging (if possible).
> +    instances_copy = list(self.instances)
> +    random.shuffle(instances_copy)
> +    for instance in instances_copy:
> +      disk_name = self.FindMatchingDisk(instance, self._disks)
> +      op_attach = opcodes.OpInstanceSetParams(
> +        instance_name=instance, hotplug_if_possible=True,
> +        disks=[(constants.DDM_ATTACH, {"name": disk_name})])
> +      op_rem = opcodes.OpInstanceSetParams(
> +        instance_name=instance, hotplug_if_possible=True,
> +        disks=[(constants.DDM_REMOVE, {})])
> +      op_stop = self.StopInstanceOp(instance)
> +      op_start = self.StartInstanceOp(instance)
> +      Log("attaching a disk with name %s" % disk_name, indent=2)
> +      Log("removing last disk", indent=2)
> +      self.ExecOrQueue(instance, [op_attach, op_rem, op_stop, op_start])
> +
> +    # Disk nodes are useful only for this test.
> +    delattr(self, "disk_nodes")
> +    delattr(self, "_disks")

Prefer 'del self.disk_nodes' for non-dynamic deletions.

> +
>    @_DoBatch(False)
>    def BurnModifyRuntimeMemory(self):
>      """Alter the runtime memory."""
> @@ -941,24 +1025,6 @@ class Burner(object):
>        Log("deactivate disks (when offline)", indent=2)
>        self.ExecOrQueue(instance, [op_act, op_stop, op_act, op_deact,
op_start])
>
> -  @_DoCheckInstances
> -  @_DoBatch(False)
> -  def BurnAddRemoveDisks(self):
> -    """Add and remove an extra disk for the instances."""
> -    Log("Adding and removing disks")
> -    for instance in self.instances:
> -      Log("instance %s", instance, indent=1)
> -      op_add = opcodes.OpInstanceSetParams(
> -        instance_name=instance,
> -        disks=[(constants.DDM_ADD, {"size": self.disk_size[0]})])
> -      op_rem = opcodes.OpInstanceSetParams(
> -        instance_name=instance, disks=[(constants.DDM_REMOVE, {})])
> -      op_stop = self.StopInstanceOp(instance)
> -      op_start = self.StartInstanceOp(instance)
> -      Log("adding a disk", indent=2)
> -      Log("removing last disk", indent=2)
> -      self.ExecOrQueue(instance, [op_add, op_stop, op_rem, op_start])
> -
>    @_DoBatch(False)
>    def BurnAddRemoveNICs(self):
>      """Add, change and remove an extra NIC for the instances."""
> @@ -1083,6 +1149,20 @@ class Burner(object):
>      try:
>        self.BurnCreateInstances()
>
> +      if self.opts.do_startstop:
> +        self.BurnStopStart()
> +
> +      # In lieu of a proper way to read the config, the
BurnCreateInstances()
> +      # function creates a mapping ('self.disk_nodes') between each
instance
> +      # and its disk nodes. This mapping is necessary for the add/remove
test,
> +      # in order to create pairs of instances and detached disks and
test the
> +      # attach functionality. However, since this mapping is static,
some tests
> +      # might change the actual instance nodes and render this mapping
useless.
> +      # Therefore, this test should run before any of these tests.
> +      if self.opts.do_addremove_disks:
> +        self.BurnAddDisks()
> +        self.BurnRemoveDisks()
> +

You could access RConfD, why prefer moving the test?

>        if self.bep[constants.BE_MINMEM] < self.bep[constants.BE_MAXMEM]:
>          self.BurnModifyRuntimeMemory()
>
> @@ -1128,9 +1208,6 @@ class Burner(object):
>        if self.opts.do_renamesame:
>          self.BurnRenameSame(self.opts.name_check, self.opts.ip_check)
>
> -      if self.opts.do_addremove_disks:
> -        self.BurnAddRemoveDisks()
> -
>        default_nic_mode = self.cluster_default_
nicparams[constants.NIC_MODE]
>        # Don't add/remove nics in routed mode, as we would need an ip to
add
>        # them with
> @@ -1149,9 +1226,6 @@ class Burner(object):
>        if self.opts.do_confd_tests:
>          self.BurnConfd()
>
> -      if self.opts.do_startstop:
> -        self.BurnStopStart()
> -
>        has_err = False
>      finally:
>        if has_err:
> --
> 1.7.10.4
>

otherwise LGTM

--
Google Germany GmbH
Dienerstr. 12
80331 München

Registergericht und -nummer: Hamburg, HRB 86891
Sitz der Gesellschaft: Hamburg
Geschäftsführer: Graham Law, Christine Elizabeth Flores

Reply via email to