From 8fb44bc93fa10fe0cfa7201060f64b5ed47ef768 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aija=20Jaunt=C4=93va?= Date: Thu, 4 Nov 2021 07:08:13 -0400 Subject: [PATCH] Add destructive field flag to export configuration New optional parameter added to export configuration method to control if destructive iDRAC settings, such as those that could cause loss of network access, are exported. It is also unlikely that these fields are necessary during import to replicate same static IP address settings. User can still put these fields back during import. Change-Id: Iad39267fedf1c1a7381d03152eb0cea0c114eede --- sushy_oem_idrac/resources/manager/manager.py | 32 +++- .../export_configuration_idrac.json | 174 ++++++++++++++++++ .../unit/resources/manager/test_manager.py | 23 +++ 3 files changed, 227 insertions(+), 2 deletions(-) create mode 100644 sushy_oem_idrac/tests/unit/json_samples/export_configuration_idrac.json diff --git a/sushy_oem_idrac/resources/manager/manager.py b/sushy_oem_idrac/resources/manager/manager.py index 6cf6dde..707abae 100644 --- a/sushy_oem_idrac/resources/manager/manager.py +++ b/sushy_oem_idrac/resources/manager/manager.py @@ -12,6 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. +import json import logging import subprocess import time @@ -42,6 +43,12 @@ _SYSTEM_CONFIG_TAG = "SystemConfiguration" # Response Code Constant _RESPONSE_OK_CODE = 200 +# Structure {'Component FQDD': (tuple of beginning of Attribute keys)} +_DESTRUCTIVE_CONF_KEYS = { + 'iDRAC.Embedded.1': + ('IPv4Static', 'IPv6Static', 'IPv4.1#Enable', 'IPv4.1#DHCPEnable', + 'IPv6.1#Enable', 'IPv6.1#AutoConfig')} + class SharedParameters(base.CompositeField): allowed_target_values = base.Field('Target@Redfish.AllowableValues') @@ -411,23 +418,44 @@ VFDD\ LOG.error('Dell OEM export system configuration failed : %s', exc) raise - def export_system_configuration(self): + def export_system_configuration(self, include_destructive_fields=True): """Export system configuration. Exports ALL targets for cloning and includes password hashes and read-only attributes. + :param include_destructive_fields: Whether includes settings such as + iDRAC static IP address that could lead to losing access to iDRAC + if importing this configuration into another system. Default to + True for backward compability. False recommended if unsure. :returns: Response object containing configuration details. :raises: InvalidParameterValueError on invalid target. :raises: ExtensionError on failure to perform requested operation """ include_in_export = mgr_cons.INCLUDE_EXPORT_READ_ONLY_PASSWORD_HASHES - return self._export_system_configuration( + + response = self._export_system_configuration( mgr_cons.EXPORT_TARGET_ALL, export_use=mgr_cons.EXPORT_USE_CLONE, include_in_export=include_in_export) + if (response.status_code == _RESPONSE_OK_CODE + and not include_destructive_fields): + conf = response.json() + if _SYSTEM_CONFIG_TAG in conf.keys(): + for fqdd, values in _DESTRUCTIVE_CONF_KEYS.items(): + for comp in conf[_SYSTEM_CONFIG_TAG]['Components']: + if comp['FQDD'] == fqdd: + attributes_copy = comp['Attributes'].copy() + for child in comp['Attributes']: + if child.get('Name').startswith(values): + attributes_copy.remove(child) + comp['Attributes'] = attributes_copy + response._content = json.dumps(conf).encode() + + return response + def get_pxe_port_macs_bios(self, ethernet_interfaces_mac): """Get a list of pxe port MAC addresses for BIOS. diff --git a/sushy_oem_idrac/tests/unit/json_samples/export_configuration_idrac.json b/sushy_oem_idrac/tests/unit/json_samples/export_configuration_idrac.json new file mode 100644 index 0000000..f1ae997 --- /dev/null +++ b/sushy_oem_idrac/tests/unit/json_samples/export_configuration_idrac.json @@ -0,0 +1,174 @@ +{ "SystemConfiguration": { + "Comments": [ + { "Comment": "Export type is Clone,RO,JSON,IncludeHash,Selective" }, + { "Comment": "Exported configuration may contain commented attributes. Attributes may be commented due to dependency, destructive nature, preserving server identity or for security reasons." } + ], + "Model": "PowerEdge R640", + "ServiceTag": "ABC1234", + "TimeStamp": "Thu Nov 4 07:21:54 2021", + "Components": [ + { "FQDD": "iDRAC.Embedded.1", + "Attributes": [ + { "Name": "Info.1#Product", + "Value": "Integrated Dell Remote Access Controller", + "Set On Import": "False", + "Comment": "Always Read Only" }, + { "Name": "Info.1#Description", + "Value": "This system component provides a complete set of remote management functions for Dell PowerEdge Servers", + "Set On Import": "False", + "Comment": "Always Read Only" }, + { "Name": "Info.1#Version", + "Value": "5.00.10.20", + "Set On Import": "False", + "Comment": "Always Read Only" }, + { "Name": "Info.1#Build", + "Value": "01", + "Set On Import": "False", + "Comment": "Always Read Only" }, + { "Name": "Info.1#Name", + "Value": "iDRAC", + "Set On Import": "False", + "Comment": "Always Read Only" }, + { "Name": "Info.1#Type", + "Value": "14G Monolithic", + "Set On Import": "False", + "Comment": "Always Read Only" }, + { "Name": "Info.1#ServerGen", + "Value": "14G", + "Set On Import": "False", + "Comment": "Always Read Only" }, + { "Name": "IPv4.1#Enable", + "Value": "Enabled", + "Set On Import": "True", + "Comment": "Read and Write" }, + { "Name": "IPv4.1#DHCPEnable", + "Value": "Disabled", + "Set On Import": "True", + "Comment": "Read and Write" }, + { "Name": "IPv6.1#Enable", + "Value": "Disabled", + "Set On Import": "True", + "Comment": "Read and Write" }, + { "Name": "IPv6.1#AutoConfig", + "Value": "Enabled", + "Set On Import": "True", + "Comment": "Read and Write" }, + { "Name": "IPv6.1#LinkLocalAddress", + "Value": "2001:db8:3333:4444:5555:6666:7777:8888", + "Set On Import": "False", + "Comment": "Always Read Only" }, + { "Name": "IPv6.1#Address2", + "Value": "::", + "Set On Import": "False", + "Comment": "Always Read Only" }, + { "Name": "IPv6.1#Address3", + "Value": "::", + "Set On Import": "False", + "Comment": "Always Read Only" }, + { "Name": "IPv6.1#Address4", + "Value": "::", + "Set On Import": "False", + "Comment": "Always Read Only" }, + { "Name": "IPv6.1#Address5", + "Value": "::", + "Set On Import": "False", + "Comment": "Always Read Only" }, + { "Name": "IPv6.1#Address6", + "Value": "::", + "Set On Import": "False", + "Comment": "Always Read Only" }, + { "Name": "IPv6.1#Address7", + "Value": "::", + "Set On Import": "False", + "Comment": "Always Read Only" }, + { "Name": "IPv6.1#Address8", + "Value": "::", + "Set On Import": "False", + "Comment": "Always Read Only" }, + { "Name": "IPv6.1#Address9", + "Value": "::", + "Set On Import": "False", + "Comment": "Always Read Only" }, + { "Name": "IPv6.1#Address10", + "Value": "::", + "Set On Import": "False", + "Comment": "Always Read Only" }, + { "Name": "IPv6.1#Address11", + "Value": "::", + "Set On Import": "False", + "Comment": "Always Read Only" }, + { "Name": "IPv6.1#Address12", + "Value": "::", + "Set On Import": "False", + "Comment": "Always Read Only" }, + { "Name": "IPv6.1#Address13", + "Value": "::", + "Set On Import": "False", + "Comment": "Always Read Only" }, + { "Name": "IPv6.1#Address14", + "Value": "::", + "Set On Import": "False", + "Comment": "Always Read Only" }, + { "Name": "IPv6.1#Address15", + "Value": "::", + "Set On Import": "False", + "Comment": "Always Read Only" }, + { "Name": "IPv6.1#AddressState", + "Value": "Active", + "Set On Import": "False", + "Comment": "Always Read Only" }, + { "Name": "IPv6.1#DUID", + "Value": "00:01:00:01:25:67:39:1b:f4:02:70:db:6c:88", + "Set On Import": "False", + "Comment": "Always Read Only" }, + { "Name": "IPv4Static.1#Address", + "Value": "192.168.88.13", + "Set On Import": "True", + "Comment": "Read and Write" }, + { "Name": "IPv4Static.1#Netmask", + "Value": "255.255.255.0", + "Set On Import": "True", + "Comment": "Read and Write" }, + { "Name": "IPv4Static.1#Gateway", + "Value": "192.168.88.1", + "Set On Import": "True", + "Comment": "Read and Write" }, + { "Name": "IPv4Static.1#DNS1", + "Value": "0.0.0.0", + "Set On Import": "True", + "Comment": "Read and Write" }, + { "Name": "IPv4Static.1#DNS2", + "Value": "0.0.0.0", + "Set On Import": "True", + "Comment": "Read and Write" }, + { "Name": "IPv4Static.1#DNSFromDHCP", + "Value": "Disabled", + "Set On Import": "True", + "Comment": "Read and Write" }, + { "Name": "IPv6Static.1#Address1", + "Value": "::", + "Set On Import": "True", + "Comment": "Read and Write" }, + { "Name": "IPv6Static.1#Gateway", + "Value": "::", + "Set On Import": "True", + "Comment": "Read and Write" }, + { "Name": "IPv6Static.1#PrefixLength", + "Value": "64", + "Set On Import": "True", + "Comment": "Read and Write" }, + { "Name": "IPv6Static.1#DNS1", + "Value": "::", + "Set On Import": "True", + "Comment": "Read and Write" }, + { "Name": "IPv6Static.1#DNS2", + "Value": "::", + "Set On Import": "True", + "Comment": "Read and Write" }, + { "Name": "IPv6Static.1#DNSFromDHCP6", + "Value": "Disabled", + "Set On Import": "True", + "Comment": "Read and Write" } + ]} + ]} + } diff --git a/sushy_oem_idrac/tests/unit/resources/manager/test_manager.py b/sushy_oem_idrac/tests/unit/resources/manager/test_manager.py index 911028c..2c05dda 100644 --- a/sushy_oem_idrac/tests/unit/resources/manager/test_manager.py +++ b/sushy_oem_idrac/tests/unit/resources/manager/test_manager.py @@ -295,6 +295,29 @@ class ManagerTestCase(BaseTestCase): export_use=mgr_cons.EXPORT_USE_CLONE, include_in_export=include_in_export) + @mock.patch('sushy.resources.oem.common._global_extn_mgrs_by_resource', {}) + def test_export_system_configuration_destructive_fields(self): + oem = self.manager.get_oem_extension('Dell') + oem._export_system_configuration = mock.Mock() + with open('sushy_oem_idrac/tests/unit/json_samples/' + 'export_configuration_idrac.json') as f: + mock_response = oem._export_system_configuration.return_value + mock_response.json.return_value = json.load(f) + mock_response.status_code = 200 + + response = oem.export_system_configuration( + include_destructive_fields=False) + + response_json = json.loads(response._content) + # From 40 items in test data 16 should be removed + self.assertEqual(24, len(response_json['SystemConfiguration'] + ['Components'][0]['Attributes'])) + include_in_export = mgr_cons.INCLUDE_EXPORT_READ_ONLY_PASSWORD_HASHES + oem._export_system_configuration.assert_called_once_with( + mgr_cons.EXPORT_TARGET_ALL, + export_use=mgr_cons.EXPORT_USE_CLONE, + include_in_export=include_in_export) + @mock.patch('sushy.resources.oem.common._global_extn_mgrs_by_resource', {}) def test_get_pxe_port_macs_bios(self): oem = self.manager.get_oem_extension('Dell')