Merge "Adding Ethernet interface for nmstate provider"

This commit is contained in:
Zuul 2023-03-20 10:23:53 +00:00 committed by Gerrit Code Review
commit 28bbc60d47
5 changed files with 1153 additions and 1 deletions

View File

@ -25,6 +25,7 @@ from os_net_config import common
from os_net_config import impl_eni
from os_net_config import impl_ifcfg
from os_net_config import impl_iproute
from os_net_config import impl_nmstate
from os_net_config import objects
from os_net_config import utils
from os_net_config import validator
@ -53,7 +54,7 @@ def parse_opts(argv):
nargs='*', default=None)
parser.add_argument('-p', '--provider', metavar='PROVIDER',
help="""The provider to use. """
"""One of: ifcfg, eni, iproute.""",
"""One of: ifcfg, eni, nmstate, iproute.""",
default=None)
parser.add_argument('-r', '--root-dir', metavar='ROOT_DIR',
help="""The root directory of the filesystem.""",
@ -171,6 +172,9 @@ def main(argv=sys.argv, main_logger=None):
elif opts.provider == 'iproute':
provider = impl_iproute.IPRouteNetConfig(noop=opts.noop,
root_dir=opts.root_dir)
elif opts.provider == 'nmstate':
provider = impl_nmstate.NmstateNetConfig(noop=opts.noop,
root_dir=opts.root_dir)
else:
main_logger.error("Invalid provider specified.")
return 1

View File

@ -0,0 +1,378 @@
# -*- coding: utf-8 -*-
# Copyright 2014-2015 Red Hat, Inc.
#
# 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 libnmstate import netapplier
from libnmstate import netinfo
from libnmstate.schema import DNS
from libnmstate.schema import Ethernet
from libnmstate.schema import Interface
from libnmstate.schema import InterfaceIPv4
from libnmstate.schema import InterfaceIPv6
from libnmstate.schema import InterfaceState
from libnmstate.schema import InterfaceType
import logging
import netaddr
import re
import yaml
import os_net_config
from os_net_config import objects
logger = logging.getLogger(__name__)
# Import the raw NetConfig object so we can call its methods
netconfig = os_net_config.NetConfig()
IPV4_DEFAULT_GATEWAY_DESTINATION = "0.0.0.0/0"
IPV6_DEFAULT_GATEWAY_DESTINATION = "::/0"
def _convert_to_bool(value):
if isinstance(value, str):
if value.lower() in ['true', 'yes', 'on']:
return True
if value.lower() in ['false', 'no', 'off']:
return False
return value
def is_dict_subset(superset, subset):
"""Check to see if one dict is a subset of another dict."""
if superset == subset:
return True
if superset and subset:
for key, value in subset.items():
if key not in superset:
return False
if isinstance(value, dict):
if not is_dict_subset(superset[key], value):
return False
elif isinstance(value, int):
if value != superset[key]:
return False
elif isinstance(value, str):
if value != superset[key]:
return False
elif isinstance(value, list):
try:
if not set(value) <= set(superset[key]):
return False
except TypeError:
for item in value:
if item not in superset[key]:
return False
elif isinstance(value, set):
if not value <= superset[key]:
return False
else:
if not value == superset[key]:
return False
return True
return False
class NmstateNetConfig(os_net_config.NetConfig):
"""Configure network interfaces using NetworkManager via nmstate API."""
def __init__(self, noop=False, root_dir=''):
super(NmstateNetConfig, self).__init__(noop, root_dir)
self.interface_data = {}
self.dns_data = {'server': [], 'domain': []}
logger.info('nmstate net config provider created.')
def __dump_config(self, config, msg="Applying config"):
cfg_dump = yaml.dump(config, default_flow_style=False,
allow_unicode=True, encoding=None)
logger.debug("----------------------------")
logger.debug(f"{msg}\n{cfg_dump}")
def iface_state(self, name=''):
"""Return the current interface state according to nmstate.
Return the current state of all interfaces, or the named interface.
:param name: name of the interface to return state, otherwise all.
:returns: list state of all interfaces when name is not specified, or
the state of the specific interface when name is specified
"""
ifaces = netinfo.show_running_config()[Interface.KEY]
if name != '':
for iface in ifaces:
if iface[Interface.NAME] != name:
continue
self.__dump_config(iface, msg=f"Running config for {name}")
return iface
else:
self.__dump_config(ifaces,
msg=f"Running config for all interfaces")
return ifaces
def cleanup_all_ifaces(self, exclude_nics=[]):
exclude_nics.extend(['lo'])
ifaces = netinfo.show_running_config()[Interface.KEY]
for iface in ifaces:
if Interface.NAME in iface and \
iface[Interface.NAME] not in exclude_nics:
iface[Interface.STATE] = InterfaceState.DOWN
state = {Interface.KEY: [iface]}
self.__dump_config(state,
msg=f"Cleaning up {iface[Interface.NAME]}")
if not self.noop:
netapplier.apply(state, verify_change=True)
def set_ifaces(self, iface_data, verify=True):
"""Apply the desired state using nmstate.
:param iface_data: interface config json
:param verify: boolean that determines if config will be verified
"""
state = {Interface.KEY: iface_data}
self.__dump_config(state, msg=f"Applying interface config")
if not self.noop:
netapplier.apply(state, verify_change=verify)
def set_dns(self, verify=True):
"""Apply the desired DNS using nmstate.
:param dns_data: config json
:param verify: boolean that determines if config will be verified
"""
state = {DNS.KEY: {DNS.CONFIG: {DNS.SERVER: self.dns_data['server'],
DNS.SEARCH: self.dns_data['domain']}}}
self.__dump_config(state, msg=f"Applying DNS")
if not self.noop:
netapplier.apply(state, verify_change=verify)
def _add_common(self, base_opt):
data = {Interface.IPV4: {InterfaceIPv4.ENABLED: False},
Interface.IPV6: {InterfaceIPv6.ENABLED: False},
Interface.NAME: base_opt.name}
if base_opt.use_dhcp:
data[Interface.IPV4][InterfaceIPv4.ENABLED] = True
data[Interface.IPV4][InterfaceIPv4.DHCP] = True
data[Interface.IPV4][InterfaceIPv4.AUTO_DNS] = True
data[Interface.IPV4][InterfaceIPv4.AUTO_ROUTES] = True
data[Interface.IPV4][InterfaceIPv4.AUTO_GATEWAY] = True
else:
data[Interface.IPV4][InterfaceIPv4.DHCP] = False
if base_opt.dns_servers:
data[Interface.IPV4][InterfaceIPv4.AUTO_DNS] = False
if base_opt.use_dhcpv6:
data[Interface.IPV6][InterfaceIPv6.ENABLED] = True
data[Interface.IPV6][InterfaceIPv6.DHCP] = True
data[Interface.IPV6][InterfaceIPv6.AUTO_DNS] = True
data[Interface.IPV6][InterfaceIPv6.AUTOCONF] = True
data[Interface.IPV6][InterfaceIPv6.AUTO_DNS] = True
data[Interface.IPV6][InterfaceIPv6.AUTO_ROUTES] = True
data[Interface.IPV6][InterfaceIPv6.AUTO_GATEWAY] = True
else:
data[Interface.IPV6][InterfaceIPv6.DHCP] = False
data[Interface.IPV6][InterfaceIPv6.AUTOCONF] = False
if base_opt.dns_servers:
data[Interface.IPV6][InterfaceIPv6.AUTO_DNS] = False
if not base_opt.defroute:
data[Interface.IPV4][InterfaceIPv4.AUTO_GATEWAY] = False
data[Interface.IPV6][InterfaceIPv6.AUTO_GATEWAY] = False
# NetworkManager always starts on boot, so set enabled state instead
if base_opt.onboot:
data[Interface.STATE] = InterfaceState.UP
else:
data[Interface.STATE] = InterfaceState.DOWN
if isinstance(base_opt, objects.Interface):
if not base_opt.hotplug:
logger.info('Using NetworkManager, hotplug is always set to'
'true. Deprecating it from next release')
elif isinstance(base_opt, objects.Vlan) or \
re.match(r'\w+\.\d+$', base_opt.name):
msg = 'Error: VLAN interfaces not yet supported by impl_nmstate'
raise os_net_config.NotImplemented(msg)
elif isinstance(base_opt, objects.IvsInterface):
msg = 'Error: IVS interfaces not yet supported by impl_nmstate'
raise os_net_config.NotImplemented(msg)
elif isinstance(base_opt, objects.NfvswitchInternal):
msg = 'Error: NFVSwitch not yet supported by impl_nmstate'
raise os_net_config.NotImplemented(msg)
elif isinstance(base_opt, objects.IbInterface):
msg = 'Error: Infiniband not yet supported by impl_nmstate'
raise os_net_config.NotImplemented(msg)
elif isinstance(base_opt, objects.OvsBond):
msg = "Error: Ovs Bonds are not yet supported by impl_nmstate"
raise os_net_config.NotImplemented(msg)
elif isinstance(base_opt, objects.LinuxBridge):
msg = "Error: Linux bridges are not yet supported by impl_nmstate"
raise os_net_config.NotImplemented(msg)
elif isinstance(base_opt, objects.LinuxTeam):
msg = "Error: Linux Teams are not yet supported by impl_nmstate"
raise os_net_config.NotImplemented(msg)
elif isinstance(base_opt, objects.OvsTunnel):
msg = "Error: OVS tunnels not yet supported by impl_nmstate"
raise os_net_config.NotImplemented(msg)
elif isinstance(base_opt, objects.OvsPatchPort):
msg = "Error: OVS tunnels not yet supported by impl_nmstate"
raise os_net_config.NotImplemented(msg)
elif isinstance(base_opt, objects.OvsDpdkBond):
msg = "Error: OVS DPDK Bonds not yet supported by impl_nmstate"
raise os_net_config.NotImplemented(msg)
else:
msg = "Error: Unsupported interface by impl_nmstate"
raise os_net_config.NotImplemented(msg)
if not base_opt.nm_controlled:
logger.info('Using NetworkManager, nm_controlled is always true.'
'Deprecating it from next release')
if base_opt.mtu:
data[Interface.MTU] = base_opt.mtu
if base_opt.addresses:
v4_addresses = base_opt.v4_addresses()
if v4_addresses:
for address in v4_addresses:
netmask_ip = netaddr.IPAddress(address.netmask)
ip_netmask = {'ip': address.ip,
'prefix-length': netmask_ip.netmask_bits()}
if InterfaceIPv4.ADDRESS not in data[Interface.IPV4]:
data[Interface.IPV4][InterfaceIPv4.ADDRESS] = []
data[Interface.IPV4][InterfaceIPv4.ENABLED] = True
data[Interface.IPV4][InterfaceIPv4.ADDRESS].append(
ip_netmask)
v6_addresses = base_opt.v6_addresses()
if v6_addresses:
for v6_address in v6_addresses:
netmask_ip = netaddr.IPAddress(v6_address.netmask)
v6ip_netmask = {'ip': v6_address.ip,
'prefix-length':
netmask_ip.netmask_bits()}
if InterfaceIPv6.ADDRESS not in data[Interface.IPV6]:
data[Interface.IPV6][InterfaceIPv6.ADDRESS] = []
data[Interface.IPV6][InterfaceIPv6.ENABLED] = True
data[Interface.IPV6][InterfaceIPv6.ADDRESS].append(
v6ip_netmask)
if base_opt.dhclient_args:
msg = "DHCP Client args not supported in impl_nmstate, ignoring"
logger.error(msg)
if base_opt.dns_servers:
self._add_dns_servers(base_opt.dns_servers)
if base_opt.domain:
self._add_dns_domain(base_opt.domain)
if base_opt.routes:
msg = "Error: Routes not yet supported by impl_nmstate"
raise os_net_config.NotImplemented(msg)
if base_opt.rules:
msg = "Error: IP Rules are not yet supported by impl_nmstate"
raise os_net_config.NotImplemented(msg)
return data
def _add_dns_servers(self, dns_servers):
for dns_server in dns_servers:
if dns_server not in self.dns_data['server']:
logger.debug(f"Adding DNS server {dns_server}")
self.dns_data['server'].append(dns_server)
def _add_dns_domain(self, dns_domain):
if isinstance(dns_domain, str):
logger.debug(f"Adding DNS domain {dns_domain}")
self.dns_data['domain'].extend([dns_domain])
return
for domain in dns_domain:
if domain not in self.dns_data['domain']:
logger.debug(f"Adding DNS domain {domain}")
self.dns_data['domain'].append(domain)
def add_interface(self, interface):
"""Add an Interface object to the net config object.
:param interface: The Interface object to add.
"""
logger.info('adding interface: %s' % interface.name)
data = self._add_common(interface)
if isinstance(interface, objects.Interface):
data[Interface.TYPE] = InterfaceType.ETHERNET
data[Ethernet.CONFIG_SUBTREE] = {}
if interface.renamed:
# TODO(Karthik S) Handle renamed interfaces
pass
if interface.hwaddr:
data[Interface.MAC] = interface.hwaddr
logger.debug('interface data: %s' % data)
self.interface_data[interface.name] = data
def apply(self, cleanup=False, activate=True):
"""Apply the network configuration.
:param cleanup: A boolean which indicates whether any undefined
(existing but not present in the object model) interface
should be disabled and deleted.
:param activate: A boolean which indicates if the config should
be activated by stopping/starting interfaces
NOTE: if cleanup is specified we will deactivate interfaces even
if activate is false
: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).
Note the noop mode is set via the constructor noop boolean
"""
logger.info('applying network configs...')
if cleanup:
logger.info('Cleaning up all network configs...')
self.cleanup_all_ifaces()
updated_interfaces = {}
logger.debug("----------------------------")
for interface_name, iface_data in self.interface_data.items():
iface_state = self.iface_state(interface_name)
if not is_dict_subset(iface_state, iface_data):
updated_interfaces[interface_name] = iface_data
else:
logger.info('No changes required for interface: %s' %
interface_name)
if activate:
if not self.noop:
try:
self.set_ifaces(list(updated_interfaces.values()))
except Exception as e:
msg = 'Error setting interfaces state: %s' % str(e)
raise os_net_config.ConfigurationError(msg)
try:
self.set_dns()
except Exception as e:
msg = 'Error setting dns servers: %s' % str(e)
raise os_net_config.ConfigurationError(msg)
if self.errors:
message = 'Failure(s) occurred when applying configuration'
logger.error(message)
for e in self.errors:
logger.error(str(e))
raise os_net_config.ConfigurationError(message)
self.interface_data = {}
return updated_interfaces

View File

@ -0,0 +1,438 @@
dns-resolver:
config: {}
hostname:
config: dell-r640-oss-14.lab.eng.brq.redhat.com
running: dell-r640-oss-14.lab.eng.brq.redhat.com
interfaces:
- accept-all-mac-addresses: false
ethernet:
auto-negotiation: true
duplex: full
speed: 1000
sr-iov:
total-vfs: 0
vfs: []
ethtool:
coalesce:
adaptive-rx: true
adaptive-tx: true
rx-usecs: 50
rx-usecs-high: 0
tx-frames-irq: 256
tx-usecs: 50
tx-usecs-high: 0
feature:
highdma: true
hw-tc-offload: false
l2-fwd-offload: false
rx-checksum: true
rx-gro: true
rx-gro-list: false
rx-hashing: true
rx-ntuple-filter: true
rx-udp-gro-forwarding: false
rx-udp_tunnel-port-offload: true
rx-vlan-hw-parse: true
tx-checksum-ip-generic: true
tx-checksum-sctp: true
tx-generic-segmentation: true
tx-gre-csum-segmentation: true
tx-gre-segmentation: true
tx-gso-partial: true
tx-ipxip4-segmentation: true
tx-ipxip6-segmentation: true
tx-nocache-copy: false
tx-tcp-ecn-segmentation: true
tx-tcp-mangleid-segmentation: false
tx-tcp-segmentation: true
tx-tcp6-segmentation: true
tx-udp-segmentation: true
tx-udp_tnl-csum-segmentation: true
tx-udp_tnl-segmentation: true
tx-vlan-hw-insert: true
pause:
autoneg: true
rx: false
tx: false
ring:
rx: 512
rx-max: 4096
tx: 512
tx-max: 4096
ipv4:
address: []
auto-dns: true
auto-gateway: true
auto-route-table-id: 0
auto-routes: true
dhcp: true
enabled: true
ipv6:
addr-gen-mode: eui64
address:
- ip: fe80::e643:4bff:fe5c:9680
prefix-length: 64
auto-dns: true
auto-gateway: true
auto-route-table-id: 0
auto-routes: true
autoconf: true
dhcp: true
enabled: true
lldp:
enabled: false
mac-address: E4:43:4B:5C:96:80
mtu: 1500
name: eno1
state: up
type: ethernet
wait-ip: any
- accept-all-mac-addresses: false
ethernet:
auto-negotiation: false
duplex: full
speed: 10000
sr-iov:
total-vfs: 0
vfs: []
ethtool:
coalesce:
adaptive-rx: true
adaptive-tx: true
rx-usecs: 50
rx-usecs-high: 0
tx-frames-irq: 256
tx-usecs: 50
tx-usecs-high: 0
feature:
highdma: true
hw-tc-offload: false
l2-fwd-offload: false
rx-checksum: true
rx-gro: true
rx-gro-list: false
rx-hashing: true
rx-ntuple-filter: true
rx-udp-gro-forwarding: false
rx-udp_tunnel-port-offload: true
rx-vlan-hw-parse: true
tx-checksum-ip-generic: true
tx-checksum-sctp: true
tx-generic-segmentation: true
tx-gre-csum-segmentation: true
tx-gre-segmentation: true
tx-gso-partial: true
tx-ipxip4-segmentation: true
tx-ipxip6-segmentation: true
tx-nocache-copy: false
tx-tcp-ecn-segmentation: true
tx-tcp-mangleid-segmentation: false
tx-tcp-segmentation: true
tx-tcp6-segmentation: true
tx-udp-segmentation: true
tx-udp_tnl-csum-segmentation: true
tx-udp_tnl-segmentation: true
tx-vlan-hw-insert: true
pause:
autoneg: false
rx: false
tx: false
ring:
rx: 512
rx-max: 4096
tx: 512
tx-max: 4096
ipv4:
enabled: false
ipv6:
enabled: false
mac-address: E4:43:4B:5C:96:81
mtu: 1500
name: eno2
state: down
type: ethernet
- accept-all-mac-addresses: false
ethernet:
auto-negotiation: false
duplex: full
speed: 10000
sr-iov:
total-vfs: 0
vfs: []
ethtool:
coalesce:
adaptive-rx: true
adaptive-tx: true
rx-usecs: 50
rx-usecs-high: 0
tx-frames-irq: 256
tx-usecs: 50
tx-usecs-high: 0
feature:
highdma: true
hw-tc-offload: false
l2-fwd-offload: false
rx-checksum: true
rx-gro: true
rx-gro-list: false
rx-hashing: true
rx-ntuple-filter: true
rx-udp-gro-forwarding: false
rx-udp_tunnel-port-offload: true
rx-vlan-hw-parse: true
tx-checksum-ip-generic: true
tx-checksum-sctp: true
tx-generic-segmentation: true
tx-gre-csum-segmentation: true
tx-gre-segmentation: true
tx-gso-partial: true
tx-ipxip4-segmentation: true
tx-ipxip6-segmentation: true
tx-nocache-copy: false
tx-tcp-ecn-segmentation: true
tx-tcp-mangleid-segmentation: false
tx-tcp-segmentation: true
tx-tcp6-segmentation: true
tx-udp-segmentation: true
tx-udp_tnl-csum-segmentation: true
tx-udp_tnl-segmentation: true
tx-vlan-hw-insert: true
pause:
autoneg: false
rx: false
tx: false
ring:
rx: 512
rx-max: 4096
tx: 512
tx-max: 4096
ipv4:
enabled: false
ipv6:
enabled: false
mac-address: E4:43:4B:5C:96:82
mtu: 1500
name: em1
state: down
type: ethernet
- accept-all-mac-addresses: false
ethernet:
auto-negotiation: false
duplex: full
speed: 10000
sr-iov:
total-vfs: 0
vfs: []
ethtool:
coalesce:
adaptive-rx: true
adaptive-tx: true
rx-usecs: 50
rx-usecs-high: 0
tx-frames-irq: 256
tx-usecs: 50
tx-usecs-high: 0
feature:
highdma: true
hw-tc-offload: false
l2-fwd-offload: false
rx-checksum: true
rx-gro: true
rx-gro-list: false
rx-hashing: true
rx-ntuple-filter: true
rx-udp-gro-forwarding: false
rx-udp_tunnel-port-offload: true
rx-vlan-hw-parse: true
tx-checksum-ip-generic: true
tx-checksum-sctp: true
tx-generic-segmentation: true
tx-gre-csum-segmentation: true
tx-gre-segmentation: true
tx-gso-partial: true
tx-ipxip4-segmentation: true
tx-ipxip6-segmentation: true
tx-nocache-copy: false
tx-tcp-ecn-segmentation: true
tx-tcp-mangleid-segmentation: false
tx-tcp-segmentation: true
tx-tcp6-segmentation: true
tx-udp-segmentation: true
tx-udp_tnl-csum-segmentation: true
tx-udp_tnl-segmentation: true
tx-vlan-hw-insert: true
pause:
autoneg: false
rx: false
tx: false
ring:
rx: 512
rx-max: 4096
tx: 512
tx-max: 4096
ipv4:
enabled: false
ipv6:
enabled: false
mac-address: E4:43:4B:5C:96:83
mtu: 1500
name: em2
state: down
type: ethernet
- accept-all-mac-addresses: false
ethernet:
auto-negotiation: true
duplex: full
speed: 40000
sr-iov:
total-vfs: 0
vfs: []
ethtool:
coalesce:
adaptive-rx: true
adaptive-tx: true
rx-frames: 128
rx-usecs: 8
tx-frames: 128
tx-usecs: 8
feature:
hw-tc-offload: true
rx-all: false
rx-checksum: true
rx-fcs: false
rx-gro: true
rx-gro-list: false
rx-hashing: true
rx-lro: false
rx-ntuple-filter: false
rx-udp-gro-forwarding: false
rx-udp_tunnel-port-offload: true
rx-vlan-filter: true
rx-vlan-hw-parse: true
tx-checksum-ip-generic: true
tx-generic-segmentation: true
tx-gre-segmentation: true
tx-gso-partial: true
tx-ipxip4-segmentation: true
tx-ipxip6-segmentation: true
tx-nocache-copy: false
tx-tcp-mangleid-segmentation: false
tx-tcp-segmentation: true
tx-tcp6-segmentation: true
tx-udp-segmentation: true
tx-udp_tnl-segmentation: true
tx-vlan-hw-insert: true
tx-vlan-stag-hw-insert: true
pause:
autoneg: false
rx: true
tx: true
ring:
rx: 1024
rx-max: 8192
tx: 1024
tx-max: 8192
ipv4:
enabled: false
ipv6:
enabled: false
mac-address: 98:03:9B:7F:9E:48
mtu: 1500
name: enp59s0f0np0
state: down
type: ethernet
- accept-all-mac-addresses: false
ethernet:
auto-negotiation: true
duplex: full
speed: 40000
sr-iov:
total-vfs: 0
vfs: []
ethtool:
coalesce:
adaptive-rx: true
adaptive-tx: true
rx-frames: 128
rx-usecs: 8
tx-frames: 128
tx-usecs: 8
feature:
hw-tc-offload: true
rx-all: false
rx-checksum: true
rx-fcs: false
rx-gro: true
rx-gro-list: false
rx-hashing: true
rx-lro: false
rx-ntuple-filter: false
rx-udp-gro-forwarding: false
rx-udp_tunnel-port-offload: true
rx-vlan-filter: true
rx-vlan-hw-parse: true
tx-checksum-ip-generic: true
tx-generic-segmentation: true
tx-gre-segmentation: true
tx-gso-partial: true
tx-ipxip4-segmentation: true
tx-ipxip6-segmentation: true
tx-nocache-copy: false
tx-tcp-mangleid-segmentation: false
tx-tcp-segmentation: true
tx-tcp6-segmentation: true
tx-udp-segmentation: true
tx-udp_tnl-segmentation: true
tx-vlan-hw-insert: true
tx-vlan-stag-hw-insert: true
pause:
autoneg: false
rx: true
tx: true
ring:
rx: 1024
rx-max: 8192
tx: 1024
tx-max: 8192
ipv4:
enabled: false
ipv6:
enabled: false
mac-address: 98:03:9B:7F:9E:49
mtu: 1500
name: enp59s0f1np1
state: down
type: ethernet
- accept-all-mac-addresses: false
ethtool:
feature:
rx-gro: true
rx-gro-list: false
rx-udp-gro-forwarding: false
tx-generic-segmentation: true
tx-gso-list: true
tx-sctp-segmentation: true
tx-tcp-ecn-segmentation: true
tx-tcp-mangleid-segmentation: true
tx-tcp-segmentation: true
tx-tcp6-segmentation: true
tx-udp-segmentation: true
ipv4:
address:
- ip: 127.0.0.1
prefix-length: 8
enabled: true
ipv6:
address:
- ip: ::1
prefix-length: 128
enabled: true
mac-address: 00:00:00:00:00:00
mtu: 65536
name: lo
state: up
type: loopback
route-rules:
config: []
routes:
config: []

View File

@ -0,0 +1,331 @@
# -*- coding: utf-8 -*-
# Copyright 2014 Red Hat, Inc.
#
# 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 os.path
import yaml
from os_net_config import impl_nmstate
from os_net_config import objects
from os_net_config.tests import base
TEST_ENV_DIR = os.path.realpath(os.path.join(os.path.dirname(__file__),
'environment'))
_BASE_IFACE_CFG = """
-
type: interface
name: em1
-
type: interface
name: eno2
addresses:
- ip_netmask: 2001:abc:a::2/64
- ip_netmask: 192.168.1.2/24
"""
_BASE_IFACE_CFG_APPLIED = """
em1:
name: em1
type: ethernet
state: up
ethernet: {}
ipv4:
enabled: false
dhcp: False
ipv6:
enabled: false
dhcp: False
autoconf: False
eno2:
name: eno2
type: ethernet
state: up
ethernet: {}
ipv4:
address:
- ip: 192.168.1.2
prefix-length: 24
dhcp: false
enabled: true
ipv6:
address:
- ip: 2001:abc:a::2
prefix-length: 64
autoconf: false
dhcp: false
enabled: true
"""
_BASE_NMSTATE_IFACE_CFG = """- name: em1
type: ethernet
state: up
"""
_NO_IP = _BASE_NMSTATE_IFACE_CFG + """ ethernet: {}
ipv4:
enabled: false
dhcp: False
ipv6:
enabled: false
dhcp: False
autoconf: False
"""
_V4_NMCFG = _BASE_NMSTATE_IFACE_CFG + """ ethernet: {}
ipv6:
enabled: False
autoconf: False
dhcp: False
ipv4:
enabled: True
dhcp: False
address:
- ip: 192.168.1.2
prefix-length: 24
"""
_V4_NMCFG_MULTIPLE = _V4_NMCFG + """ - ip: 192.168.2.2
prefix-length: 32
- ip: 10.0.0.2
prefix-length: 8
"""
_V4_NMCFG_MAPPED = _V4_NMCFG + """
802-3-Ethernet.cloned-mac-address: a1:b2:c3:d4:e5
"""
_V4_V6_NMCFG = _BASE_NMSTATE_IFACE_CFG + """ ipv6:
enabled: True
autoconf: False
dhcp: False
address:
- ip: 2001:abc:a::2
prefix-length: 64
ipv4:
enabled: True
dhcp: False
address:
- ip: 192.168.1.2
prefix-length: 24
ethernet: {}
"""
_V6_NMCFG = _BASE_NMSTATE_IFACE_CFG + """ ethernet: {}
ipv4:
enabled: False
dhcp: False
ipv6:
enabled: True
autoconf: False
dhcp: False
address:
- ip: "2001:abc:a::"
prefix-length: 64
"""
_V6_NMCFG_MULTIPLE = _V6_NMCFG + """ - ip: 2001:abc:b::1
prefix-length: 64
- ip: 2001:abc:c::2
prefix-length: 96
"""
class TestNmstateNetConfig(base.TestCase):
def setUp(self):
super(TestNmstateNetConfig, self).setUp()
self.provider = impl_nmstate.NmstateNetConfig()
def stub_is_ovs_installed():
return True
self.stub_out('os_net_config.utils.is_ovs_installed',
stub_is_ovs_installed)
def get_interface_config(self, name='em1'):
return self.provider.interface_data[name]
def get_dns_data(self):
return self.provider.dns_data
def test_add_base_interface(self):
interface = objects.Interface('em1')
self.provider.add_interface(interface)
self.assertEqual(yaml.safe_load(_NO_IP)[0],
self.get_interface_config())
def test_add_interface_with_v6(self):
v6_addr = objects.Address('2001:abc:a::/64')
interface = objects.Interface('em1', addresses=[v6_addr])
self.provider.add_interface(interface)
self.assertEqual(yaml.safe_load(_V6_NMCFG)[0],
self.get_interface_config())
def test_add_interface_with_v4_v6(self):
addresses = [objects.Address('2001:abc:a::2/64'),
objects.Address('192.168.1.2/24')]
interface = objects.Interface('em1', addresses=addresses)
self.provider.add_interface(interface)
self.assertEqual(yaml.safe_load(_V4_V6_NMCFG)[0],
self.get_interface_config())
def test_add_interface_with_v6_multiple(self):
addresses = [objects.Address('2001:abc:a::/64'),
objects.Address('2001:abc:b::1/64'),
objects.Address('2001:abc:c::2/96')]
interface = objects.Interface('em1', addresses=addresses)
self.provider.add_interface(interface)
self.assertEqual(yaml.safe_load(_V6_NMCFG_MULTIPLE)[0],
self.get_interface_config())
def test_interface_defroute(self):
interface1 = objects.Interface('em1')
interface2 = objects.Interface('em2', defroute=False)
self.provider.add_interface(interface1)
self.provider.add_interface(interface2)
em1_config = """- name: em1
type: ethernet
state: up
ethernet: {}
ipv4:
enabled: False
dhcp: False
ipv6:
enabled: False
autoconf: False
dhcp: False
"""
em2_config = """- name: em2
type: ethernet
state: up
ethernet: {}
ipv4:
enabled: False
auto-gateway: False
dhcp: False
ipv6:
auto-gateway: False
enabled: False
autoconf: False
dhcp: False
"""
self.assertEqual(yaml.safe_load(em1_config)[0],
self.get_interface_config('em1'))
self.assertEqual(yaml.safe_load(em2_config)[0],
self.get_interface_config('em2'))
def test_interface_dns_server(self):
interface1 = objects.Interface('em1', dns_servers=['1.2.3.4'])
self.provider.add_interface(interface1)
em1_config = """- name: em1
type: ethernet
state: up
ethernet: {}
ipv4:
auto-dns: False
enabled: False
dhcp: False
ipv6:
auto-dns: False
enabled: False
autoconf: False
dhcp: False
"""
test_dns_config1 = """
server:
- 1.2.3.4
domain: []
"""
self.assertEqual(yaml.safe_load(em1_config)[0],
self.get_interface_config('em1'))
self.assertEqual(yaml.safe_load(test_dns_config1),
self.get_dns_data())
interface2 = objects.Interface('em2',
dns_servers=['1.2.3.4',
'2001:4860:4860::8888'],
domain=['example.com', 'server.org'])
self.provider.add_interface(interface2)
test_dns_config2 = """
server:
- 1.2.3.4
- 2001:4860:4860::8888
domain:
- example.com
- server.org
"""
self.assertEqual(yaml.safe_load(test_dns_config2),
self.get_dns_data())
interface3 = objects.Interface('em3',
dns_servers=['1.2.3.4',
'2001:4860:4860::8888'],
domain='testdomain.com')
self.provider.add_interface(interface3)
test_dns_config3 = """
server:
- 1.2.3.4
- 2001:4860:4860::8888
domain:
- example.com
- server.org
- testdomain.com
"""
self.assertEqual(yaml.safe_load(test_dns_config3),
self.get_dns_data())
class TestNmstateNetConfigApply(base.TestCase):
def setUp(self):
super(TestNmstateNetConfigApply, self).setUp()
def test_iface_state(iface_data='', verify_change=True):
# This function returns None
return None
self.stub_out(
'libnmstate.netapplier.apply', test_iface_state)
self.provider = impl_nmstate.NmstateNetConfig()
def add_object(self, nic_config):
iface_array = yaml.safe_load(nic_config)
for iface_json in iface_array:
obj = objects.object_from_json(iface_json)
self.provider.add_object(obj)
def get_running_info(self, yaml_file):
with open(yaml_file) as f:
data = yaml.load(f, Loader=yaml.SafeLoader)
return data
def tearDown(self):
super(TestNmstateNetConfigApply, self).tearDown()
def test_base_interface(self):
def show_running_info_stub():
running_info_path = os.path.join(
os.path.dirname(__file__),
'environment/netinfo_running_info_1.yaml')
running_info = self.get_running_info(running_info_path)
return running_info
self.stub_out('libnmstate.netinfo.show_running_config',
show_running_info_stub)
self.add_object(_BASE_IFACE_CFG)
updated_files = self.provider.apply()
self.assertEqual(yaml.load(_BASE_IFACE_CFG_APPLIED,
Loader=yaml.SafeLoader),
updated_files)

View File

@ -7,6 +7,7 @@ ignore_basepython_conflict = True
[testenv]
usedevelop = True
basepython = python3
sitepackages = True
setenv =
VIRTUAL_ENV={envdir}
deps =