From e6aed8bb073a49b5ae262f4c5b989d09d3c06d5c Mon Sep 17 00:00:00 2001 From: Glenn Matthews Date: Wed, 8 Mar 2017 17:27:09 -0500 Subject: [PATCH] Remove extraneous config profiles when cloning an Item. Fixes #64. --- CHANGELOG.rst | 7 ++ COT/commands/tests/test_edit_hardware.py | 107 ++++++++++++++++++++++- COT/vm_description/ovf/hardware.py | 10 +++ COT/vm_description/ovf/name_helper.py | 2 +- 4 files changed, 124 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 498c47d..2e518db 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,6 +6,12 @@ This project adheres to `Semantic Versioning`_. `Unreleased`_ ------------- +**Fixed** + +- Fixed a case that could result in a RuntimeError being thrown when using + ``cot edit-hardware`` to simultaneously create NICs and define a new + configuration profile (`#64`_). + **Added** - ``Command`` (formerly ``COTGenericSubmodule``) now checks the available @@ -708,6 +714,7 @@ Initial public release. .. _#61: https://github.com/glennmatthews/cot/issues/61 .. _#62: https://github.com/glennmatthews/cot/issues/62 .. _#63: https://github.com/glennmatthews/cot/issues/63 +.. _#64: https://github.com/glennmatthews/cot/issues/64 .. _Semantic Versioning: http://semver.org/ .. _`PEP 8`: https://www.python.org/dev/peps/pep-0008/ diff --git a/COT/commands/tests/test_edit_hardware.py b/COT/commands/tests/test_edit_hardware.py index d0d8ead..1836050 100644 --- a/COT/commands/tests/test_edit_hardware.py +++ b/COT/commands/tests/test_edit_hardware.py @@ -657,7 +657,7 @@ def test_set_nic_count_merge_profiles(self): """) def test_set_nic_count_create_new_one_profile(self): - """"Create a new NIC under a single profile.""" + """Create a new NIC under a single profile.""" self.command.package = self.input_ovf self.command.nics = '4' self.command.profiles = ['4CPU-4GB-3NIC'] @@ -675,6 +675,85 @@ def test_set_nic_count_create_new_one_profile(self): + 14 + VMXNET3 + 10 ++ + +""") + + def test_set_nic_count_create_new_and_new_profile(self): + """Create new NICs under a new profile. Test for issue #64.""" + self.command.package = self.input_ovf + self.command.nics = '4' + self.command.profiles = ['4CPU-4GB-4NIC'] + self.command.run() + self.command.finished() + self.check_diff(""" + ++ ++ 4CPU-4GB-4NIC ++ 4CPU-4GB-4NIC ++ + +... + +- ++ + 12 +... + +- ++ + 13 +... + 13 ++ VMXNET3 ++ 10 ++ ++ ++ 14 ++ true ++ VM Network ++ VMXNET3 ethernet adapter on "VM Network"\ + ++ Ethernet4 ++ 14 + VMXNET3 +""") + + def test_set_nic_count_create_new_and_split_new_profile(self): + """Create new NICs under a new profile splitting from unified profile. + + Another test for issue #64. + """ + self.command.package = self.csr_ovf + self.command.nics = '4' + self.command.profiles = ['4CPU-4GB-4NIC'] + self.command.run() + self.command.finished() + self.check_diff(file1=self.csr_ovf, expected=""" + ++ ++ Data network 4 ++ + +... + Large hardware profile (requires purchase of DRAM \ +upgrade SKU) - 4 vCPUs, 8 GB RAM ++ ++ ++ 4CPU-4GB-4NIC ++ 4CPU-4GB-4NIC + +... + ++ ++ 14 ++ true ++ GigabitEthernet4 ++ NIC representing GigabitEthernet4 ++ GigabitEthernet4 ++ 14 ++ VMXNET3 virtio ++ 10 + """) @@ -693,6 +772,32 @@ def test_set_nic_count_delete_nics(self): 11 """) + def test_set_nic_count_delete_nics_new_profile(self): + """Set NIC count to a lower value under a newly created profile.""" + self.command.package = self.csr_ovf + self.command.nics = 1 + self.command.profiles = ['1CPU-4GB-1NIC'] + self.command.run() + self.command.finished() + self.check_diff(file1=self.csr_ovf, expected=""" + ++ ++ 1CPU-4GB-1NIC ++ 1CPU-4GB-1NIC ++ + +... + +- ++ + 12 +... + +- ++ + 13 +""") + def test_set_nic_count_no_existing(self): """Create a NIC when nothing pre-exists.""" self.command.package = self.minimal_ovf diff --git a/COT/vm_description/ovf/hardware.py b/COT/vm_description/ovf/hardware.py index 3d3fa5d..0b0bd97 100644 --- a/COT/vm_description/ovf/hardware.py +++ b/COT/vm_description/ovf/hardware.py @@ -205,7 +205,17 @@ def clone_item(self, parent_item, profile_list): tuple: ``(instance_id, ovfitem)`` """ instance = self.find_unused_instance_id() + logger.debug("Cloning existing Item %s with new instance ID %s", + parent_item, instance) ovfitem = copy.deepcopy(parent_item) + + # Delete any profiles from the parent that we don't need now, + # otherwise we'll get an error when trying to set the instance ID + # on our clone due to self-inconsistency (#64). + for profile in self.ovf.config_profiles: + if ovfitem.has_profile(profile) and profile not in profile_list: + ovfitem.remove_profile(profile) + ovfitem.set_property(self.ovf.INSTANCE_ID, instance, profile_list) ovfitem.modified = True self.item_dict[instance] = ovfitem diff --git a/COT/vm_description/ovf/name_helper.py b/COT/vm_description/ovf/name_helper.py index fc372d5..8ecad19 100644 --- a/COT/vm_description/ovf/name_helper.py +++ b/COT/vm_description/ovf/name_helper.py @@ -283,7 +283,7 @@ def __getattr__(self, name): elif name == "EPASD" or name == "SASD": self._cache[name] = self.RASD elif name not in self._raw: - raise AttributeError + raise AttributeError("Unknown attribute '{0}'".format(name)) else: ns = getattr(self, self._raw[name].namespace_name) tag = self._raw[name].tag