From 462436fa4d339c80a888def1707f792c33cca715 Mon Sep 17 00:00:00 2001 From: Dan Prince Date: Thu, 14 Aug 2014 14:05:05 -0400 Subject: Add --cleanup, and impl for ifcfg Adds a new cleanup option which can be used to ifdown and remove interfaces that exists but aren't specified in the object model (or JSON). --- os_net_config/__init__.py | 5 +++- os_net_config/cli.py | 9 ++++++- os_net_config/impl_eni.py | 2 +- os_net_config/impl_ifcfg.py | 45 +++++++++++++++++++++++++++------- os_net_config/tests/test_impl_ifcfg.py | 12 +++++++++ 5 files changed, 61 insertions(+), 12 deletions(-) diff --git a/os_net_config/__init__.py b/os_net_config/__init__.py index bd28bbc..9e451f2 100644 --- a/os_net_config/__init__.py +++ b/os_net_config/__init__.py @@ -56,10 +56,13 @@ class NetConfig(object): def add_bond(self, bond): raise NotImplemented("add_bond is not implemented.") - def apply(self, noop=False): + def apply(self, noop=False, cleanup=False): """Apply the network configuration. :param noop: A boolean which indicates whether this is a no-op. + :param cleanup: A boolean which indicates whether any undefined + (existing but not present in the object model) interfaces + should be disabled and deleted. :returns: a dict of the format: filename/data which contains info for each file that was changed (or would be changed if in --noop mode). diff --git a/os_net_config/cli.py b/os_net_config/cli.py index 0efc399..5b6ff1d 100644 --- a/os_net_config/cli.py +++ b/os_net_config/cli.py @@ -64,6 +64,13 @@ def parse_opts(argv): help="Return the configuration commands, without applying them.", required=False) + parser.add_argument( + '--cleanup', + dest="cleanup", + action='store_true', + help="Cleanup unconfigured interfaces.", + required=False) + opts = parser.parse_args(argv[1:]) return opts @@ -122,7 +129,7 @@ def main(argv=sys.argv): for iface_json in iface_array: obj = objects.object_from_json(iface_json) provider.addObject(obj) - files_changed = provider.apply(noop=opts.noop) + files_changed = provider.apply(noop=opts.noop, cleanup=opts.cleanup) if opts.noop: for location, data in files_changed.iteritems(): print "File: %s\n" % location diff --git a/os_net_config/impl_eni.py b/os_net_config/impl_eni.py index cf09ff3..2a2e4e1 100644 --- a/os_net_config/impl_eni.py +++ b/os_net_config/impl_eni.py @@ -164,7 +164,7 @@ class ENINetConfig(os_net_config.NetConfig): self.routes[interface_name] = data logger.debug('route data: %s' % self.routes[interface_name]) - def apply(self, noop=False): + def apply(self, noop=False, cleanup=False): """Apply the network configuration. :param noop: A boolean which indicates whether this is a no-op. diff --git a/os_net_config/impl_ifcfg.py b/os_net_config/impl_ifcfg.py index bd1cf22..4125017 100644 --- a/os_net_config/impl_ifcfg.py +++ b/os_net_config/impl_ifcfg.py @@ -14,7 +14,9 @@ # License for the specific language governing permissions and limitations # under the License. +import glob import logging +import os import os_net_config from os_net_config import objects @@ -40,6 +42,10 @@ def route_config_path(name): return "/etc/sysconfig/network-scripts/route-%s" % name +def cleanup_pattern(): + return "/etc/sysconfig/network-scripts/ifcfg-*" + + class IfcfgNetConfig(os_net_config.NetConfig): """Configure network interfaces using the ifcfg format.""" @@ -166,10 +172,13 @@ class IfcfgNetConfig(os_net_config.NetConfig): if bond.routes: self._add_routes(bond.name, bond.routes) - def apply(self, noop=False): + def apply(self, noop=False, cleanup=False): """Apply the network configuration. :param noop: A boolean which indicates whether this is a no-op. + :param cleanup: A boolean which indicates whether any undefined + (existing but not present in the object model) interfaces + should be disabled and deleted. :returns: a dict of the format: filename/data which contains info for each file that was changed (or would be changed if in --noop mode). @@ -178,29 +187,47 @@ class IfcfgNetConfig(os_net_config.NetConfig): restart_interfaces = [] restart_bridges = [] update_files = {} + all_file_names = [] for interface_name, iface_data in self.interfaces.iteritems(): route_data = self.routes.get(interface_name, '') - if (utils.diff(ifcfg_config_path(interface_name), iface_data) or - utils.diff(route_config_path(interface_name), route_data)): + interface_path = ifcfg_config_path(interface_name) + route_path = route_config_path(interface_name) + all_file_names.append(interface_path) + all_file_names.append(route_path) + if (utils.diff(interface_path, iface_data) or + utils.diff(route_path, route_data)): restart_interfaces.append(interface_name) - update_files[ifcfg_config_path(interface_name)] = iface_data - update_files[route_config_path(interface_name)] = route_data + update_files[interface_path] = iface_data + update_files[route_path] = route_data logger.info('No changes required for interface: %s' % interface_name) for bridge_name, bridge_data in self.bridges.iteritems(): route_data = self.routes.get(bridge_name, '') - if (utils.diff(ifcfg_config_path(bridge_name), bridge_data) or - utils.diff(route_config_path(bridge_name), route_data)): + bridge_path = bridge_config_path(bridge_name) + bridge_route_path = route_config_path(bridge_name) + all_file_names.append(bridge_path) + all_file_names.append(bridge_route_path) + if (utils.diff(bridge_path, bridge_data) or + utils.diff(bridge_route_path, route_data)): restart_bridges.append(bridge_name) - update_files[bridge_config_path(bridge_name)] = bridge_data - update_files[route_config_path(bridge_name)] = route_data + update_files[bridge_path] = bridge_data + update_files[bridge_route_path] = route_data logger.info('No changes required for bridge: %s' % bridge_name) if noop: return update_files + if cleanup: + for ifcfg_file in glob.iglob(cleanup_pattern()): + if ifcfg_file not in all_file_names: + interface_name = ifcfg_file[37:] + logger.info('cleaning up interface: %s' % interface_name) + processutils.execute('/sbin/ifdown', interface_name, + check_exit_code=False) + os.remove(ifcfg_file) + for interface in restart_interfaces: logger.info('running ifdown on interface: %s' % interface) processutils.execute('/sbin/ifdown', interface, diff --git a/os_net_config/tests/test_impl_ifcfg.py b/os_net_config/tests/test_impl_ifcfg.py index aee34d3..a5c621c 100644 --- a/os_net_config/tests/test_impl_ifcfg.py +++ b/os_net_config/tests/test_impl_ifcfg.py @@ -14,6 +14,7 @@ # License for the specific language governing permissions and limitations # under the License. +import os.path import tempfile from os_net_config import impl_ifcfg @@ -220,6 +221,7 @@ class TestIfcfgNetConfigApply(base.TestCase): self.temp_ifcfg_file = tempfile.NamedTemporaryFile() self.temp_route_file = tempfile.NamedTemporaryFile() self.temp_bridge_file = tempfile.NamedTemporaryFile() + self.temp_cleanup_file = tempfile.NamedTemporaryFile() def test_ifcfg_path(name): return self.temp_ifcfg_file.name @@ -233,6 +235,10 @@ class TestIfcfgNetConfigApply(base.TestCase): return self.temp_bridge_file.name self.stubs.Set(impl_ifcfg, 'bridge_config_path', test_bridge_path) + def test_cleanup_pattern(): + return self.temp_cleanup_file.name + self.stubs.Set(impl_ifcfg, 'cleanup_pattern', test_cleanup_pattern) + def test_execute(*args, **kwargs): pass self.stubs.Set(processutils, 'execute', test_execute) @@ -243,6 +249,8 @@ class TestIfcfgNetConfigApply(base.TestCase): self.temp_ifcfg_file.close() self.temp_route_file.close() self.temp_bridge_file.close() + if os.path.exists(self.temp_cleanup_file.name): + self.temp_cleanup_file.close() super(TestIfcfgNetConfigApply, self).tearDown() def test_network_apply(self): @@ -282,3 +290,7 @@ class TestIfcfgNetConfigApply(base.TestCase): ifcfg_data = utils.get_file_data(self.temp_ifcfg_file.name) self.assertEqual(_VLAN_NO_IP, ifcfg_data) + + def test_cleanup(self): + self.provider.apply(cleanup=True) + self.assertTrue(not os.path.exists(self.temp_cleanup_file.name)) -- cgit 1.2.3-korg