On Thu, Nov 06, 2014 at 12:30:44AM +0200, Alex Pyrgiotis wrote:
> Extend _ApplyContainerMods to accept an attach and detach function.
> These functions operate in the same way as the add and remove functions,
> minus the disk creation and disk removal parts.
>
> Also, add two functions that should raise an exception if one attempts
> to attach/detach a NIC.
>
> Finally, update the tests in order to work with the new
> _ApplyContainerMods function.
>
> Signed-off-by: Alex Pyrgiotis <[email protected]>
>
> diff --git a/lib/cmdlib/instance_set_params.py b/lib/cmdlib/instance_set_
params.py
> index a4cc576..65e2a6c 100644
> --- a/lib/cmdlib/instance_set_params.py
> +++ b/lib/cmdlib/instance_set_params.py
> @@ -58,7 +58,7 @@ from ganeti.cmdlib.instance_storage import
CalculateFileStorageDir, \
>    ComputeDiskSizePerVG, ComputeDisksInfo, CreateDisks, \
>    CreateSingleBlockDev, GenerateDiskTemplate, \
>    IsExclusiveStorageEnabledNodeUuid, ShutdownInstanceDisks, \
> -  WaitForSync, WipeOrCleanupDisks
> +  WaitForSync, WipeOrCleanupDisks, AssembleInstanceDisks
>  from ganeti.cmdlib.instance_utils import BuildInstanceHookEnvByObject, \
>    NICToTuple, CheckNodeNotDrained, CopyLockList, \
>    ReleaseLocks, CheckNodeVmCapable, CheckTargetNodeIPolicy, \
> @@ -783,8 +783,8 @@ class LUInstanceSetParams(LogicalUnit):
>      # Verify disk changes (operating on a copy)
>      inst_disks = self.cfg.GetInstanceDisks(self.instance.uuid)
>      disks = copy.deepcopy(inst_disks)
> -    ApplyContainerMods("disk", disks, None, self.diskmod, None,
> -                       _PrepareDiskMod, None)
> +    ApplyContainerMods("disk", disks, None, self.diskmod, None, None,
> +                       _PrepareDiskMod, None, None)
>      utils.ValidateDeviceNames("disk", disks)
>      if len(disks) > constants.MAX_DISKS:
>        raise errors.OpPrereqError("Instance has too many disks (%d),
cannot add"
> @@ -1160,6 +1160,10 @@ class LUInstanceSetParams(LogicalUnit):
>                                     {}, cluster, pnode_uuid)
>        return (None, None)
>
> +    def _PrepareNicAttach(_, __, ___):
> +      raise errors.OpPrereqError("Attach operation is not supported for
NICs",
> +                                 errors.ECODE_INVAL)
> +
>      def _PrepareNicMod(_, nic, params, private):
>        self._PrepareNicModification(params, private, nic.ip, nic.network,
>                                     nic.nicparams, cluster, pnode_uuid)
> @@ -1171,10 +1175,15 @@ class LUInstanceSetParams(LogicalUnit):
>        if net is not None and ip is not None:
>          self.cfg.ReleaseIp(net, ip, self.proc.GetECId())
>
> +    def _PrepareNicDetach(_, __, ___):
> +      raise errors.OpPrereqError("Detach operation is not supported for
NICs",
> +                                 errors.ECODE_INVAL)
> +
>      # Verify NIC changes (operating on copy)
>      nics = [nic.Copy() for nic in self.instance.nics]
> -    ApplyContainerMods("NIC", nics, None, self.nicmod,
> -                       _PrepareNicCreate, _PrepareNicMod,
_PrepareNicRemove)
> +    ApplyContainerMods("NIC", nics, None, self.nicmod, _PrepareNicCreate,
> +                       _PrepareNicAttach, _PrepareNicMod,
_PrepareNicRemove,
> +                       _PrepareNicDetach)
>      if len(nics) > constants.MAX_NICS:
>        raise errors.OpPrereqError("Instance has too many network
interfaces"
>                                   " (%d), cannot add more" %
constants.MAX_NICS,
> @@ -1186,8 +1195,8 @@ class LUInstanceSetParams(LogicalUnit):
>        # Operate on copies as this is still in prereq
>        nics = [nic.Copy() for nic in self.instance.nics]
>        ApplyContainerMods("NIC", nics, self._nic_chgdesc, self.nicmod,
> -                         self._CreateNewNic, self._ApplyNicMods,
> -                         self._RemoveNic)
> +                         self._CreateNewNic, None, self._ApplyNicMods,
> +                         self._RemoveNic, None)
>        # Verify that NIC names are unique and valid
>        utils.ValidateDeviceNames("NIC", nics)
>        self._new_nics = nics
> @@ -1588,6 +1597,36 @@ class LUInstanceSetParams(LogicalUnit):
>      if not self.instance.disks_active:
>        ShutdownInstanceDisks(self, self.instance, disks=[disk])
>
> +  def _AttachDisk(self, idx, params, _):
> +    """Attaches an existing disk to an instance.
> +
> +    """
> +    disk = self.GenericGetDiskInfo(**params) # pylint: disable=W0142

see above.

> +    self.cfg.AttachInstanceDisk(self.instance.uuid, disk, idx)
> +
> +    # re-read the instance from the configuration
> +    self.instance = self.cfg.GetInstanceInfo(self.instance.uuid)
> +
> +    changes = [
> +      ("disk/%d" % idx,
> +       "attach:size=%s,mode=%s" % (disk.size, disk.mode)),
> +      ]
> +
> +    if self.op.hotplug:
> +      disks_ok, _, results = AssembleInstanceDisks(self, self.instance,
> +                                                   disks=[disk])
> +      if not disks_ok:
> +        changes.append(("disk/%d" % idx, "assemble:failed"))
> +        return disk, changes
> +
> +      _, link_name, uri = results[0].payload
> +      msg = self._HotplugDevice(constants.HOTPLUG_ACTION_ADD,
> +                                constants.HOTPLUG_TARGET_DISK,
> +                                disk, (link_name, uri), idx)
> +      changes.append(("disk/%d" % idx, msg))
> +
> +    return (disk, changes)
> +
>    def _ModifyDisk(self, idx, disk, params, _):
>      """Modifies a disk.
>
> @@ -1645,6 +1684,27 @@ class LUInstanceSetParams(LogicalUnit):
>
>      return hotmsg
>
> +  def _DetachDisk(self, idx, root, _):
> +    """Detaches a disk from an instance.
> +
> +    """
> +    hotmsg = ""
> +    if self.op.hotplug:
> +      hotmsg = self._HotplugDevice(constants.HOTPLUG_ACTION_REMOVE,
> +                                   constants.HOTPLUG_TARGET_DISK,
> +                                   root, None, idx)
> +
> +    # Always shutdown the disk before detaching.
> +    ShutdownInstanceDisks(self, self.instance, [root])
> +
> +    # Remove disk from config
> +    self.cfg.DetachInstanceDisk(self.instance.uuid, root.uuid)
> +
> +    # re-read the instance from the configuration
> +    self.instance = self.cfg.GetInstanceInfo(self.instance.uuid)
> +
> +    return hotmsg
> +
>    def _CreateNewNic(self, idx, params, private):
>      """Creates data structure for a new network interface.
>
> @@ -1746,8 +1806,9 @@ class LUInstanceSetParams(LogicalUnit):
>      # Apply disk changes
>      inst_disks = self.cfg.GetInstanceDisks(self.instance.uuid)
>      ApplyContainerMods("disk", inst_disks, result, self.diskmod,
> -                       self._CreateNewDisk, self._ModifyDisk,
> -                       self._RemoveDisk, post_add_fn=self._PostAddDisk)
> +                       self._CreateNewDisk, self._AttachDisk,
self._ModifyDisk,
> +                       self._RemoveDisk, self._DetachDisk,
> +                       post_add_fn=self._PostAddDisk)
>
>      if self.op.disk_template:
>        if __debug__:
> diff --git a/lib/cmdlib/instance_utils.py b/lib/cmdlib/instance_utils.py
> index f3b6a40..5a9849c 100644
> --- a/lib/cmdlib/instance_utils.py
> +++ b/lib/cmdlib/instance_utils.py
> @@ -873,8 +873,8 @@ def PrepareContainerMods(mods, private_fn):
>
>
>  def ApplyContainerMods(kind, container, chgdesc, mods,
> -                       create_fn, modify_fn, remove_fn,
> -                       post_add_fn=None):
> +                       create_fn, attach_fn, modify_fn, remove_fn,
> +                       detach_fn, post_add_fn=None):
>    """Applies descriptions in C{mods} to C{container}.
>
>    @type kind: string
> @@ -890,6 +890,10 @@ def ApplyContainerMods(kind, container, chgdesc,
mods,
>      receives absolute item index, parameters and private data object as
added
>      by L{PrepareContainerMods}, returns tuple containing new item and
changes
>      as list
> +  @type attach_fn: callable
> +  @param attach_fn: Callback for attaching an existing item to a
container
> +    (L{constants.DDM_ATTACH}); receives absolute item index and item
UUID or
> +    name, returns tuple containing new item and changes as list
>    @type modify_fn: callable
>    @param modify_fn: Callback for modifying an existing item
>      (L{constants.DDM_MODIFY}); receives absolute item index, item,
parameters
> @@ -898,6 +902,9 @@ def ApplyContainerMods(kind, container, chgdesc, mods,
>    @type remove_fn: callable
>    @param remove_fn: Callback on removing item; receives absolute item
index,
>      item and private data object as added by L{PrepareContainerMods}
> +  @type detach_fn: callable
> +  @param detach_fn: Callback on detaching item; receives absolute item
index,
> +    item and private data object as added by L{PrepareContainerMods}
>    @type post_add_fn: callable
>    @param post_add_fn: Callable for post-processing a newly created item
after
>      it has been put into the container. It receives the index of the new
item
> @@ -919,6 +926,18 @@ def ApplyContainerMods(kind, container, chgdesc,
mods,
>        if post_add_fn is not None:
>          post_add_fn(addidx, item)
>
> +    elif op == constants.DDM_ATTACH:
> +      addidx = GetIndexFromIdentifier(identifier, kind, container)
> +      if attach_fn is None:
> +        item = params
> +      else:
> +        (item, changes) = attach_fn(addidx, params, private)
> +
> +      InsertItemToIndex(int(identifier), item, container)
> +
> +      if post_add_fn is not None:
> +        post_add_fn(addidx, item)
> +
>      else:
>        # Retrieve existing item
>        (absidx, item) = GetItemFromContainer(identifier, kind, container)
> @@ -935,6 +954,18 @@ def ApplyContainerMods(kind, container, chgdesc,
mods,
>
>          assert container[absidx] == item
>          del container[absidx]
> +      elif op == constants.DDM_DETACH:
> +        assert not params
> +
> +        changes = [("%s/%s" % (kind, absidx), "detach")]
> +
> +        if detach_fn is not None:
> +          msg = detach_fn(absidx, item, private)
> +          if msg:
> +            changes.append(("%s/%s" % (kind, absidx), msg))
> +
> +        assert container[absidx] == item
> +        del container[absidx]
>        elif op == constants.DDM_MODIFY:
>          if modify_fn is not None:
>            changes = modify_fn(absidx, item, params, private)
> diff --git a/test/py/cmdlib/instance_unittest.py
b/test/py/cmdlib/instance_unittest.py
> index bc44dd4..32c7496 100644
> --- a/test/py/cmdlib/instance_unittest.py
> +++ b/test/py/cmdlib/instance_unittest.py
> @@ -963,7 +963,7 @@ class TestApplyContainerMods(unittest.TestCase):
>      container = []
>      chgdesc = []
>      instance_utils.ApplyContainerMods("test", container, chgdesc, [],
None,
> -                                      None, None)
> +                                      None, None, None, None)
>      self.assertEqual(container, [])
>      self.assertEqual(chgdesc, [])
>
> @@ -976,8 +976,8 @@ class TestApplyContainerMods(unittest.TestCase):
>        (constants.DDM_ADD, 0, "Start"),
>        (constants.DDM_ADD, -1, "End"),
>        ], None)
> -    instance_utils.ApplyContainerMods("test", container, chgdesc, mods,
> -                                      None, None, None)
> +instance_utils.ApplyContainerMods("test", container, chgdesc, mods,
> +                                  None, None, None, None, None)
>      self.assertEqual(container, ["Start", "Hello", "World", "End"])
>      self.assertEqual(chgdesc, [])
>
> @@ -987,8 +987,8 @@ class TestApplyContainerMods(unittest.TestCase):
>        (constants.DDM_ADD, 5, "four"),
>        (constants.DDM_ADD, 7, "xyz"),
>        ], None)
> -    instance_utils.ApplyContainerMods("test", container, chgdesc, mods,
> -                                      None, None, None)
> +instance_utils.ApplyContainerMods("test", container, chgdesc, mods,
> +                                  None, None, None, None, None)
>      self.assertEqual(container,
>                       ["zero", "Start", "Hello", "Added", "World", "four",
>                        "End", "xyz"])
> @@ -999,7 +999,8 @@ class TestApplyContainerMods(unittest.TestCase):
>          (constants.DDM_ADD, idx, "error"),
>          ], None)
>        self.assertRaises(IndexError, instance_utils.ApplyContainerMods,
> -                        "test", container, None, mods, None, None, None)
> +                        "test", container, None, mods, None, None, None,
None,
> +                        None)
>
>    def testRemoveError(self):
>      for idx in [0, 1, 2, 100, -1, -4]:
> @@ -1007,13 +1008,13 @@ class TestApplyContainerMods(unittest.TestCase):
>          (constants.DDM_REMOVE, idx, None),
>          ], None)
>        self.assertRaises(IndexError, instance_utils.ApplyContainerMods,
> -                        "test", [], None, mods, None, None, None)
> +                        "test", [], None, mods, None, None, None, None,
None)
>
>      mods = instance_utils.PrepareContainerMods([
>        (constants.DDM_REMOVE, 0, object()),
>        ], None)
>      self.assertRaises(AssertionError, instance_utils.ApplyContainerMods,
> -                      "test", [""], None, mods, None, None, None)
> +                      "test", [""], None, mods, None, None, None, None,
None)
>
>    def testAddError(self):
>      for idx in range(-100, -1) + [100]:
> @@ -1021,7 +1022,7 @@ class TestApplyContainerMods(unittest.TestCase):
>          (constants.DDM_ADD, idx, None),
>          ], None)
>        self.assertRaises(IndexError, instance_utils.ApplyContainerMods,
> -                        "test", [], None, mods, None, None, None)
> +                        "test", [], None, mods, None, None, None, None,
None)
>
>    def testRemove(self):
>      container = ["item 1", "item 2"]
> @@ -1031,8 +1032,8 @@ class TestApplyContainerMods(unittest.TestCase):
>        (constants.DDM_ADD, -1, "bbb"),
>        ], None)
>      chgdesc = []
> -    instance_utils.ApplyContainerMods("test", container, chgdesc, mods,
> -                                      None, None, None)
> +instance_utils.ApplyContainerMods("test", container, chgdesc, mods,
> +                                  None, None, None, None, None)
>      self.assertEqual(container, ["item 1", "item 2", "bbb"])
>      self.assertEqual(chgdesc, [
>        ("test/2", "remove"),
> @@ -1046,8 +1047,8 @@ class TestApplyContainerMods(unittest.TestCase):
>        (constants.DDM_MODIFY, 1, "c"),
>        ], None)
>      chgdesc = []
> -    instance_utils.ApplyContainerMods("test", container, chgdesc, mods,
> -                                      None, None, None)
> +instance_utils.ApplyContainerMods("test", container, chgdesc, mods,
> +                                  None, None, None, None, None)
>      self.assertEqual(container, ["item 1", "item 2"])
>      self.assertEqual(chgdesc, [])
>
> @@ -1056,7 +1057,8 @@ class TestApplyContainerMods(unittest.TestCase):
>          (constants.DDM_MODIFY, idx, "error"),
>          ], None)
>        self.assertRaises(IndexError, instance_utils.ApplyContainerMods,
> -                        "test", container, None, mods, None, None, None)
> +                        "test", container, None, mods, None, None, None,
None,
> +                        None)
>
>    @staticmethod
>    def _CreateTestFn(idx, params, private):
> @@ -1090,8 +1092,8 @@ class TestApplyContainerMods(unittest.TestCase):
>        (constants.DDM_ADD, 1, "More"),
>        ], mock.Mock)
>      instance_utils.ApplyContainerMods("test", container, chgdesc, mods,
> -                                      self._CreateTestFn,
self._ModifyTestFn,
> -                                      self._RemoveTestFn)
> +                                      self._CreateTestFn, None,
self._ModifyTestFn,
> +                                      self._RemoveTestFn, None)
>      self.assertEqual(container, [
>        (000, "Start"),
>        (100, "More"),
> --
> 1.7.10.4
>


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