Merge "Adding Ethernet interface for nmstate provider"
This commit is contained in:
commit
28bbc60d47
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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: []
|
|
@ -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)
|
Loading…
Reference in New Issue