Initial ifcfg implementation for interfaces/routes

Ifcfg formatted persistence for interfaces and routes.
This commit is contained in:
Dan Prince
2014-06-09 16:38:34 -04:00
parent 846e00d007
commit af110352f8
5 changed files with 238 additions and 4 deletions

View File

@@ -17,3 +17,17 @@ import pbr.version
__version__ = pbr.version.VersionInfo( __version__ = pbr.version.VersionInfo(
'os_net_config').version_string() '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.")

View File

@@ -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

View File

@@ -1,3 +1,5 @@
# -*- coding: utf-8 -*-
# Licensed under the Apache License, Version 2.0 (the "License"); you may # 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 # not use this file except in compliance with the License. You may obtain
# a copy of the License at # a copy of the License at
@@ -20,9 +22,10 @@ class NetworkObjectException(Exception):
class Route(object): class Route(object):
"""Base class for network routes.""" """Base class for network routes."""
def __init__(self, netmask_cidr, gateway): def __init__(self, next_hop, ip_netmask="", default=False):
self.netmask_cidr = netmask_cidr self.next_hop = next_hop
self.gateway = gateway self.ip_netmask = ip_netmask
self.default = default
class Address(object): class Address(object):
@@ -41,10 +44,11 @@ class Interface(object):
"""Base class for network interfaces.""" """Base class for network interfaces."""
def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=[], def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=[],
mtu=1500): routes=[], mtu=1500):
self.name = name self.name = name
self.mtu = mtu self.mtu = mtu
self.use_dhcp = use_dhcp self.use_dhcp = use_dhcp
self.use_dhcpv6 = use_dhcpv6
self.addresses = addresses self.addresses = addresses
self.bridge = None self.bridge = None
self.type = None self.type = None

View File

@@ -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())

View File

@@ -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)