summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Prince <dprince@redhat.com>2014-06-09 16:38:34 -0400
committerDan Prince <dprince@redhat.com>2014-06-09 16:38:34 -0400
commitaf110352f8f61f68c4758f2c8c0846f9691e6bf1 (patch)
tree80b11a4f75873f4b6eb4aaf2235c979357cf4d46
parent846e00d0072533e817c3d2248834e3b3ac5cae98 (diff)
Initial ifcfg implementation for interfaces/routes
Ifcfg formatted persistence for interfaces and routes.
-rw-r--r--os_net_config/__init__.py14
-rw-r--r--os_net_config/impl_ifcfg.py93
-rw-r--r--os_net_config/objects.py12
-rw-r--r--os_net_config/tests/test_impl_ifcfg.py82
-rw-r--r--os_net_config/tests/test_objects.py41
5 files changed, 238 insertions, 4 deletions
diff --git a/os_net_config/__init__.py b/os_net_config/__init__.py
index dbc60ab..bdbc69f 100644
--- a/os_net_config/__init__.py
+++ b/os_net_config/__init__.py
@@ -17,3 +17,17 @@ import pbr.version
__version__ = pbr.version.VersionInfo(
'os_net_config').version_string()
+
+
+class NotImplemented(Exception):
+ pass
+
+
+class NetworkConfig(object):
+ """Configure network interfaces using the ifcfg format."""
+
+ def addInterface(self, interface):
+ raise NotImplemented("addInterface is not implemented.")
+
+ def addRoutes(self, interface_name, routes=[]):
+ raise NotImplemented("addRoutes is not implemented.")
diff --git a/os_net_config/impl_ifcfg.py b/os_net_config/impl_ifcfg.py
new file mode 100644
index 0000000..c4fbde4
--- /dev/null
+++ b/os_net_config/impl_ifcfg.py
@@ -0,0 +1,93 @@
+# -*- coding: utf-8 -*-
+
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+
+def ifcfg_config_path(name):
+ return "/etc/sysconfig/network-scripts/ifcfg-%s" % name
+
+
+def route_config_path(name):
+ return "/etc/sysconfig/network-scripts/route-%s" % name
+
+
+def writeConfig(filename, data):
+ with open(filename, "w") as f:
+ f.write(str(data))
+
+
+def get_config(filename):
+ with open(filename, "r") as f:
+ return f.read()
+
+
+def diff(filename, data):
+ return get_config(filename) == data
+
+
+class IfcfgNetwork(object):
+ """Configure network interfaces using the ifcfg format."""
+
+ def __init__(self):
+ self.interfaces = {}
+ self.routes = {}
+
+ def addInterface(self, interface):
+ data = "DEVICE=%s\n" % interface.name
+ data += "ONBOOT=yes\n"
+ data += "HOTPLUG=no\n"
+ if interface.type == 'ovs':
+ data += "DEVICETYPE=ovs\n"
+ if interface.bridge:
+ data += "TYPE=OVSPort\n"
+ data += "OVS_BRIDGE=%s\n" % interface.bridge
+ data += "BOOTPROTO=none\n"
+ if interface.mtu != 1500:
+ data += "MTU=%i\n" % interface.mtu
+ if interface.use_dhcp:
+ data += "BOOTPROTO=dhcp\n"
+ if interface.use_dhcpv6 or interface.v6_addresses():
+ data += "IPV6INIT=yes\n"
+ if interface.mtu != 1500:
+ data += "IPV6_MTU=%i\n" % interface.mtu
+ if interface.use_dhcpv6:
+ data += "DHCPV6C=yes\n"
+ elif interface.addresses:
+ #TODO(dprince): support multiple addresses for each type
+ v4_addresses = interface.v4_addresses()
+ if v4_addresses:
+ first_v4 = v4_addresses[0]
+ data += "BOOTPROTO=static\n"
+ data += "IPADDR=%s\n" % first_v4.ip
+ data += "NETMASK=%s\n" % first_v4.netmask
+
+ v6_addresses = interface.v6_addresses()
+ if v6_addresses:
+ first_v6 = v6_addresses[0]
+ data += "IPV6_AUTOCONF=no\n"
+ data += "IPV6ADDR=%s\n" % first_v6.ip
+
+ self.interfaces[interface.name] = data
+
+ def addRoutes(self, interface_name, routes=[]):
+ data = ""
+ first_line = ""
+ for route in routes:
+ if route.default:
+ first_line = "default %s dev %s\n" % (route.next_hop,
+ interface_name)
+ else:
+ data += "%s via %s dev %s" % (route.ip_netmask,
+ route.next_hop,
+ interface_name)
+ self.routes[interface_name] == first_line + data
diff --git a/os_net_config/objects.py b/os_net_config/objects.py
index 8c1fcd5..455e55f 100644
--- a/os_net_config/objects.py
+++ b/os_net_config/objects.py
@@ -1,3 +1,5 @@
+# -*- coding: utf-8 -*-
+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
@@ -20,9 +22,10 @@ class NetworkObjectException(Exception):
class Route(object):
"""Base class for network routes."""
- def __init__(self, netmask_cidr, gateway):
- self.netmask_cidr = netmask_cidr
- self.gateway = gateway
+ def __init__(self, next_hop, ip_netmask="", default=False):
+ self.next_hop = next_hop
+ self.ip_netmask = ip_netmask
+ self.default = default
class Address(object):
@@ -41,10 +44,11 @@ class Interface(object):
"""Base class for network interfaces."""
def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=[],
- mtu=1500):
+ routes=[], mtu=1500):
self.name = name
self.mtu = mtu
self.use_dhcp = use_dhcp
+ self.use_dhcpv6 = use_dhcpv6
self.addresses = addresses
self.bridge = None
self.type = None
diff --git a/os_net_config/tests/test_impl_ifcfg.py b/os_net_config/tests/test_impl_ifcfg.py
new file mode 100644
index 0000000..7a2deec
--- /dev/null
+++ b/os_net_config/tests/test_impl_ifcfg.py
@@ -0,0 +1,82 @@
+# -*- coding: utf-8 -*-
+
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import tempfile
+
+from os_net_config import impl_ifcfg
+from os_net_config import objects
+from os_net_config.tests import base
+
+
+_BASE_IFCFG = """DEVICE=foo
+ONBOOT=yes
+HOTPLUG=no
+"""
+
+_V4_IFCFG = _BASE_IFCFG + """BOOTPROTO=static
+IPADDR=192.168.1.1
+NETMASK=255.255.255.0
+"""
+
+_V6_IFCFG = _BASE_IFCFG + """IPV6INIT=yes
+IPV6_AUTOCONF=no
+IPV6ADDR=2001:abc:a::
+"""
+
+_OVS_IFCFG = _BASE_IFCFG + "DEVICETYPE=ovs\n"
+
+
+_OVS_BRIDGE_IFCFG = _BASE_IFCFG + "DEVICETYPE=ovs\n"
+
+
+class TestIfcfgNetwork(base.TestCase):
+
+ def setUp(self):
+ super(TestIfcfgNetwork, self).setUp()
+ self.temp_config_file = tempfile.NamedTemporaryFile()
+
+ def test_config_path(name):
+ return self.temp_config_file.name
+ self.stubs.Set(impl_ifcfg, 'ifcfg_config_path', test_config_path)
+ self.provider = impl_ifcfg.IfcfgNetwork()
+
+ def tearDown(self):
+ self.temp_config_file.close()
+ super(TestIfcfgNetwork, self).tearDown()
+
+ def get_interface_config(self):
+ return self.provider.interfaces['foo']
+
+ def test_add_base_interface(self):
+ interface = objects.Interface('foo')
+ self.provider.addInterface(interface)
+ self.assertEqual(_BASE_IFCFG, self.get_interface_config())
+
+ def test_add_ovs_interface(self):
+ interface = objects.Interface('foo')
+ interface.type = 'ovs'
+ self.provider.addInterface(interface)
+ self.assertEqual(_OVS_IFCFG, self.get_interface_config())
+
+ def test_add_interface_with_v4(self):
+ v4_addr = objects.Address('192.168.1.1/24')
+ interface = objects.Interface('foo', addresses=[v4_addr])
+ self.provider.addInterface(interface)
+ self.assertEqual(_V4_IFCFG, self.get_interface_config())
+
+ def test_add_interface_with_v6(self):
+ v6_addr = objects.Address('2001:abc:a::/64')
+ interface = objects.Interface('foo', addresses=[v6_addr])
+ self.provider.addInterface(interface)
+ self.assertEqual(_V6_IFCFG, self.get_interface_config())
diff --git a/os_net_config/tests/test_objects.py b/os_net_config/tests/test_objects.py
new file mode 100644
index 0000000..7ffd5b3
--- /dev/null
+++ b/os_net_config/tests/test_objects.py
@@ -0,0 +1,41 @@
+# -*- coding: utf-8 -*-
+
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from os_net_config import objects
+from os_net_config.tests import base
+
+
+class TestAddress(base.TestCase):
+
+ def test_ipv4_address(self):
+ address = objects.Address('192.168.1.1/24')
+ self.assertEqual("192.168.1.1", address.ip)
+ self.assertEqual("255.255.255.0", address.netmask)
+ self.assertEqual(4, address.version)
+
+ def test_ipv6_address(self):
+ address = objects.Address('2001:abc:a::/64')
+ self.assertEqual("2001:abc:a::", address.ip)
+ self.assertEqual("ffff:ffff:ffff:ffff::", address.netmask)
+ self.assertEqual(6, address.version)
+
+
+class TestInterface(base.TestCase):
+
+ def test_interface_addresses(self):
+ v4_addr = objects.Address('192.168.1.1/24')
+ v6_addr = objects.Address('2001:abc:a::/64')
+ interface = objects.Interface('foo', addresses=[v4_addr, v6_addr])
+ self.assertEquals("192.168.1.1", interface.v4_addresses()[0].ip)
+ self.assertEquals("2001:abc:a::", interface.v6_addresses()[0].ip)