cloud-init-dev team mailing list archive
-
cloud-init-dev team
-
Mailing list archive
-
Message #06173
[Merge] ~goneri/cloud-init:freebsd_net_renderer into cloud-init:master
Gonéri Le Bouder has proposed merging ~goneri/cloud-init:freebsd_net_renderer into cloud-init:master.
Requested reviews:
cloud-init commiters (cloud-init-dev)
For more details, see:
https://code.launchpad.net/~goneri/cloud-init/+git/cloud-init/+merge/365641
--
Your team cloud-init commiters is requested to review the proposed merge of ~goneri/cloud-init:freebsd_net_renderer into cloud-init:master.
diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py
index 20c994d..c453234 100644
--- a/cloudinit/distros/__init__.py
+++ b/cloudinit/distros/__init__.py
@@ -145,7 +145,7 @@ class Distro(object):
# Write it out
# pylint: disable=assignment-from-no-return
- # We have implementations in arch, freebsd and gentoo still
+ # We have implementations in arch and gentoo still
dev_names = self._write_network(settings)
# pylint: enable=assignment-from-no-return
# Now try to bring them up
diff --git a/cloudinit/distros/freebsd.py b/cloudinit/distros/freebsd.py
index ff22d56..2ab11a5 100644
--- a/cloudinit/distros/freebsd.py
+++ b/cloudinit/distros/freebsd.py
@@ -15,10 +15,6 @@ from cloudinit import helpers
from cloudinit import log as logging
from cloudinit import ssh_util
from cloudinit import util
-
-from cloudinit.distros import net_util
-from cloudinit.distros.parsers.resolv_conf import ResolvConf
-
from cloudinit.settings import PER_INSTANCE
LOG = logging.getLogger(__name__)
@@ -274,309 +270,8 @@ class Distro(distros.Distro):
keys = set(kwargs['ssh_authorized_keys']) or []
ssh_util.setup_user_keys(keys, name, options=None)
- @staticmethod
- def get_ifconfig_list():
- cmd = ['ifconfig', '-l']
- (nics, err) = util.subp(cmd, rcs=[0, 1])
- if len(err):
- LOG.warning("Error running %s: %s", cmd, err)
- return None
- return nics
-
- @staticmethod
- def get_ifconfig_ifname_out(ifname):
- cmd = ['ifconfig', ifname]
- (if_result, err) = util.subp(cmd, rcs=[0, 1])
- if len(err):
- LOG.warning("Error running %s: %s", cmd, err)
- return None
- return if_result
-
- @staticmethod
- def get_ifconfig_ether():
- cmd = ['ifconfig', '-l', 'ether']
- (nics, err) = util.subp(cmd, rcs=[0, 1])
- if len(err):
- LOG.warning("Error running %s: %s", cmd, err)
- return None
- return nics
-
- @staticmethod
- def get_interface_mac(ifname):
- if_result = Distro.get_ifconfig_ifname_out(ifname)
- for item in if_result.splitlines():
- if item.find('ether ') != -1:
- mac = str(item.split()[1])
- if mac:
- return mac
-
- @staticmethod
- def get_devicelist():
- nics = Distro.get_ifconfig_list()
- return nics.split()
-
- @staticmethod
- def get_ipv6():
- ipv6 = []
- nics = Distro.get_devicelist()
- for nic in nics:
- if_result = Distro.get_ifconfig_ifname_out(nic)
- for item in if_result.splitlines():
- if item.find("inet6 ") != -1 and item.find("scopeid") == -1:
- ipv6.append(nic)
- return ipv6
-
- def get_ipv4(self):
- ipv4 = []
- nics = Distro.get_devicelist()
- for nic in nics:
- if_result = Distro.get_ifconfig_ifname_out(nic)
- for item in if_result.splitlines():
- print(item)
- if self.ipv4_pat.match(item):
- ipv4.append(nic)
- return ipv4
-
- def is_up(self, ifname):
- if_result = Distro.get_ifconfig_ifname_out(ifname)
- pat = "^" + ifname
- for item in if_result.splitlines():
- if re.match(pat, item):
- flags = item.split('<')[1].split('>')[0]
- if flags.find("UP") != -1:
- return True
-
- def _get_current_rename_info(self, check_downable=True):
- """Collect information necessary for rename_interfaces."""
- names = Distro.get_devicelist()
- bymac = {}
- for n in names:
- bymac[Distro.get_interface_mac(n)] = {
- 'name': n, 'up': self.is_up(n), 'downable': None}
-
- nics_with_addresses = set()
- if check_downable:
- nics_with_addresses = set(self.get_ipv4() + self.get_ipv6())
-
- for d in bymac.values():
- d['downable'] = (d['up'] is False or
- d['name'] not in nics_with_addresses)
-
- return bymac
-
- def _rename_interfaces(self, renames):
- if not len(renames):
- LOG.debug("no interfaces to rename")
- return
-
- current_info = self._get_current_rename_info()
-
- cur_bymac = {}
- for mac, data in current_info.items():
- cur = data.copy()
- cur['mac'] = mac
- cur_bymac[mac] = cur
-
- def update_byname(bymac):
- return dict((data['name'], data)
- for data in bymac.values())
-
- def rename(cur, new):
- util.subp(["ifconfig", cur, "name", new], capture=True)
-
- def down(name):
- util.subp(["ifconfig", name, "down"], capture=True)
-
- def up(name):
- util.subp(["ifconfig", name, "up"], capture=True)
-
- ops = []
- errors = []
- ups = []
- cur_byname = update_byname(cur_bymac)
- tmpname_fmt = "cirename%d"
- tmpi = -1
-
- for mac, new_name in renames:
- cur = cur_bymac.get(mac, {})
- cur_name = cur.get('name')
- cur_ops = []
- if cur_name == new_name:
- # nothing to do
- continue
-
- if not cur_name:
- errors.append("[nic not present] Cannot rename mac=%s to %s"
- ", not available." % (mac, new_name))
- continue
-
- if cur['up']:
- msg = "[busy] Error renaming mac=%s from %s to %s"
- if not cur['downable']:
- errors.append(msg % (mac, cur_name, new_name))
- continue
- cur['up'] = False
- cur_ops.append(("down", mac, new_name, (cur_name,)))
- ups.append(("up", mac, new_name, (new_name,)))
-
- if new_name in cur_byname:
- target = cur_byname[new_name]
- if target['up']:
- msg = "[busy-target] Error renaming mac=%s from %s to %s."
- if not target['downable']:
- errors.append(msg % (mac, cur_name, new_name))
- continue
- else:
- cur_ops.append(("down", mac, new_name, (new_name,)))
-
- tmp_name = None
- while tmp_name is None or tmp_name in cur_byname:
- tmpi += 1
- tmp_name = tmpname_fmt % tmpi
-
- cur_ops.append(("rename", mac, new_name, (new_name, tmp_name)))
- target['name'] = tmp_name
- cur_byname = update_byname(cur_bymac)
- if target['up']:
- ups.append(("up", mac, new_name, (tmp_name,)))
-
- cur_ops.append(("rename", mac, new_name, (cur['name'], new_name)))
- cur['name'] = new_name
- cur_byname = update_byname(cur_bymac)
- ops += cur_ops
-
- opmap = {'rename': rename, 'down': down, 'up': up}
- if len(ops) + len(ups) == 0:
- if len(errors):
- LOG.debug("unable to do any work for renaming of %s", renames)
- else:
- LOG.debug("no work necessary for renaming of %s", renames)
- else:
- LOG.debug("achieving renaming of %s with ops %s",
- renames, ops + ups)
-
- for op, mac, new_name, params in ops + ups:
- try:
- opmap.get(op)(*params)
- except Exception as e:
- errors.append(
- "[unknown] Error performing %s%s for %s, %s: %s" %
- (op, params, mac, new_name, e))
- if len(errors):
- raise Exception('\n'.join(errors))
-
- def apply_network_config_names(self, netcfg):
- renames = []
- for ent in netcfg.get('config', {}):
- if ent.get('type') != 'physical':
- continue
- mac = ent.get('mac_address')
- name = ent.get('name')
- if not mac:
- continue
- renames.append([mac, name])
- return self._rename_interfaces(renames)
-
- @classmethod
- def generate_fallback_config(self):
- nics = Distro.get_ifconfig_ether()
- if nics is None:
- LOG.debug("Fail to get network interfaces")
- return None
- potential_interfaces = nics.split()
- connected = []
- for nic in potential_interfaces:
- pat = "^" + nic
- if_result = Distro.get_ifconfig_ifname_out(nic)
- for item in if_result.split("\n"):
- if re.match(pat, item):
- flags = item.split('<')[1].split('>')[0]
- if flags.find("RUNNING") != -1:
- connected.append(nic)
- if connected:
- potential_interfaces = connected
- names = list(sorted(potential_interfaces))
- default_pri_nic = Distro.default_primary_nic
- if default_pri_nic in names:
- names.remove(default_pri_nic)
- names.insert(0, default_pri_nic)
- target_name = None
- target_mac = None
- for name in names:
- mac = Distro.get_interface_mac(name)
- if mac:
- target_name = name
- target_mac = mac
- break
- if target_mac and target_name:
- nconf = {'config': [], 'version': 1}
- nconf['config'].append(
- {'type': 'physical', 'name': target_name,
- 'mac_address': target_mac, 'subnets': [{'type': 'dhcp'}]})
- return nconf
- else:
- return None
-
- def _write_network(self, settings):
- entries = net_util.translate_network(settings)
- nameservers = []
- searchdomains = []
- dev_names = entries.keys()
- for (device, info) in entries.items():
- # Skip the loopback interface.
- if device.startswith('lo'):
- continue
-
- dev = self.getnetifname(device)
-
- LOG.info('Configuring interface %s', dev)
-
- if info.get('bootproto') == 'static':
- LOG.debug('Configuring dev %s with %s / %s', dev,
- info.get('address'), info.get('netmask'))
- # Configure an ipv4 address.
- ifconfig = (info.get('address') + ' netmask ' +
- info.get('netmask'))
-
- # Configure the gateway.
- self.updatercconf('defaultrouter', info.get('gateway'))
-
- if 'dns-nameservers' in info:
- nameservers.extend(info['dns-nameservers'])
- if 'dns-search' in info:
- searchdomains.extend(info['dns-search'])
- else:
- ifconfig = 'DHCP'
-
- self.updatercconf('ifconfig_' + dev, ifconfig)
-
- # Try to read the /etc/resolv.conf or just start from scratch if that
- # fails.
- try:
- resolvconf = ResolvConf(util.load_file(self.resolv_conf_fn))
- resolvconf.parse()
- except IOError:
- util.logexc(LOG, "Failed to parse %s, use new empty file",
- self.resolv_conf_fn)
- resolvconf = ResolvConf('')
- resolvconf.parse()
-
- # Add some nameservers
- for server in nameservers:
- try:
- resolvconf.add_nameserver(server)
- except ValueError:
- util.logexc(LOG, "Failed to add nameserver %s", server)
-
- # And add any searchdomains.
- for domain in searchdomains:
- try:
- resolvconf.add_search_domain(domain)
- except ValueError:
- util.logexc(LOG, "Failed to add search domain %s", domain)
- util.write_file(self.resolv_conf_fn, str(resolvconf), 0o644)
-
- return dev_names
+ def _write_network_config(self, netconfig):
+ return self._supported_write_network_config(netconfig)
def apply_locale(self, locale, out_fn=None):
# Adjust the locals value to the new value
@@ -604,18 +299,9 @@ class Distro(distros.Distro):
util.logexc(LOG, "Failed to restore %s backup",
self.login_conf_fn)
- def _bring_up_interface(self, device_name):
- if device_name.startswith('lo'):
- return
- dev = self.getnetifname(device_name)
- cmd = ['/etc/rc.d/netif', 'start', dev]
- LOG.debug("Attempting to bring up interface %s using command %s",
- dev, cmd)
- # This could return 1 when the interface has already been put UP by the
- # OS. This is just fine.
- (_out, err) = util.subp(cmd, rcs=[0, 1])
- if len(err):
- LOG.warning("Error running %s: %s", cmd, err)
+ def apply_network_config_names(self, netconfig):
+ # This is handled by the freebsd network renderer.
+ return
def install_packages(self, pkglist):
self.update_package_sources()
diff --git a/cloudinit/net/freebsd.py b/cloudinit/net/freebsd.py
new file mode 100644
index 0000000..595541c
--- /dev/null
+++ b/cloudinit/net/freebsd.py
@@ -0,0 +1,190 @@
+# This file is part of cloud-init. See LICENSE file for license information.
+
+import os
+import re
+from six import StringIO
+
+from cloudinit import log as logging
+from cloudinit import util
+from cloudinit.distros.parsers.resolv_conf import ResolvConf
+
+from . import renderer
+
+LOG = logging.getLogger(__name__)
+
+
+class Renderer(renderer.Renderer):
+ rc_conf_fn = '/etc/rc.conf'
+ resolv_conf_fn = '/etc/resolv.conf'
+
+ # Updates a key in /etc/rc.conf.
+ # TODO(Goneri): Duplicated with distros.freebsd
+ def updatercconf(self, key, value):
+ LOG.debug("Checking %s for: %s = %s", self.rc_conf_fn, key, value)
+ conf = self.loadrcconf()
+ config_changed = False
+ if key not in conf:
+ LOG.debug("Adding key in %s: %s = %s", self.rc_conf_fn, key,
+ value)
+ conf[key] = value
+ config_changed = True
+ else:
+ for item in conf.keys():
+ if item == key and conf[item] != value:
+ conf[item] = value
+ LOG.debug("Changing key in %s: %s = %s", self.rc_conf_fn,
+ key, value)
+ config_changed = True
+
+ if config_changed:
+ LOG.info("Writing %s", self.rc_conf_fn)
+ buf = StringIO()
+ for keyval in conf.items():
+ buf.write('%s="%s"\n' % keyval)
+ util.write_file(self.rc_conf_fn, buf.getvalue())
+
+ # Load the contents of /etc/rc.conf and store all keys in a dict. Make sure
+ # quotes are ignored:
+ # hostname="bla"
+ # TODO(Goneri): Duplicated with distros.freebsd
+ def loadrcconf(self):
+ RE_MATCH = re.compile(r'^(\w+)\s*=\s*(.*)\s*')
+ conf = {}
+ lines = util.load_file(self.rc_conf_fn).splitlines()
+ for line in lines:
+ m = RE_MATCH.match(line)
+ if not m:
+ LOG.debug("Skipping line from /etc/rc.conf: %s", line)
+ continue
+ key = m.group(1).rstrip()
+ val = m.group(2).rstrip()
+ # Kill them quotes (not completely correct, aka won't handle
+ # quoted values, but should be ok ...)
+ if val[0] in ('"', "'"):
+ val = val[1:]
+ if val[-1] in ('"', "'"):
+ val = val[0:-1]
+ if len(val) == 0:
+ LOG.debug("Skipping empty value from /etc/rc.conf: %s", line)
+ continue
+ conf[key] = val
+ return conf
+
+ # TODO(Goneri): Duplicated with distros.freebsd
+ def readrcconf(self, key):
+ conf = self.loadrcconf()
+ try:
+ val = conf[key]
+ except KeyError:
+ val = None
+ return val
+
+ def __init__(self, config=None):
+ if not config:
+ config = {}
+ self.rcconf_path = config.get('rcconf_path', 'etc/rc.conf')
+
+ def _render_route(self, route, indent=""):
+ pass
+
+ def _render_iface(self, iface, render_hwaddress=False):
+ pass
+
+ def _ifconfig_a(self):
+ (out, _) = util.subp(['ifconfig', '-a'])
+ return out
+
+ def _get_ifname_by_mac(self, mac):
+ out = self._ifconfig_a()
+ blocks = re.split(r'(^\S+|\n\S+):', out)
+ blocks.reverse()
+ blocks.pop() # Ignore the first one
+ while blocks:
+ ifname = blocks.pop()
+ m = re.search(r'ether\s([\da-f:]{17})', blocks.pop())
+ if m and m.group(1) == mac:
+ return ifname
+
+ def _write_network(self, settings):
+ nameservers = []
+ searchdomains = []
+ for interface in settings.iter_interfaces():
+ device_name = interface.get("name")
+ device_mac = interface.get("mac_address")
+ if device_name:
+ if re.match(r'^lo\d+$', device_name):
+ continue
+ if device_mac and device_name:
+ cur_name = self._get_ifname_by_mac(device_mac)
+ if not cur_name:
+ LOG.info('Cannot find any device with MAC %s', device_mac)
+ continue
+ if cur_name != device_name:
+ self.updatercconf(
+ 'ifconfig_%s_name' % cur_name,
+ device_name)
+ elif device_mac:
+ device_name = self._get_ifname_by_mac(device_mac)
+
+ subnet = interface.get("subnets", [])[0]
+ LOG.info('Configuring interface %s', device_name)
+
+ if subnet.get('type') == 'static':
+ LOG.debug('Configuring dev %s with %s / %s', device_name,
+ subnet.get('address'), subnet.get('netmask'))
+ # Configure an ipv4 address.
+ ifconfig = (subnet.get('address') + ' netmask ' +
+ subnet.get('netmask'))
+
+ # Configure the gateway.
+ self.updatercconf('defaultrouter', subnet.get('gateway'))
+
+ if 'dns_nameservers' in subnet:
+ nameservers.extend(subnet['dns_nameservers'])
+ if 'dns_search' in subnet:
+ searchdomains.extend(subnet['dns_search'])
+ else:
+ ifconfig = 'DHCP'
+
+ self.updatercconf('ifconfig_' + device_name, ifconfig)
+ # Note: We don't try to be cleaver beceause if an interface
+ # is renamed, we must reload the netif.
+ util.subp(['/etc/rc.d/netif', 'restart'])
+ util.subp(['/etc/rc.d/routing', 'restart'])
+
+ # Try to read the /etc/resolv.conf or just start from scratch if that
+ # fails.
+ try:
+ resolvconf = ResolvConf(util.load_file(self.resolv_conf_fn))
+ resolvconf.parse()
+ except IOError:
+ util.logexc(LOG, "Failed to parse %s, use new empty file",
+ self.resolv_conf_fn)
+ resolvconf = ResolvConf('')
+ resolvconf.parse()
+
+ # Add some nameservers
+ for server in nameservers:
+ try:
+ resolvconf.add_nameserver(server)
+ except ValueError:
+ util.logexc(LOG, "Failed to add nameserver %s", server)
+
+ # And add any searchdomains.
+ for domain in searchdomains:
+ try:
+ resolvconf.add_search_domain(domain)
+ except ValueError:
+ util.logexc(LOG, "Failed to add search domain %s", domain)
+ util.write_file(self.resolv_conf_fn, str(resolvconf), 0o644)
+
+ def render_network_state(self, network_state, templates=None, target=None):
+ self._write_network(network_state)
+
+
+def available(target=None):
+ rcconf_path = util.target_path(target, 'etc/rc.conf')
+ if not os.path.isfile(rcconf_path):
+ return False
+
+ return True
diff --git a/cloudinit/net/renderers.py b/cloudinit/net/renderers.py
index 5117b4a..b98dbbe 100644
--- a/cloudinit/net/renderers.py
+++ b/cloudinit/net/renderers.py
@@ -1,17 +1,19 @@
# This file is part of cloud-init. See LICENSE file for license information.
from . import eni
+from . import freebsd
from . import netplan
from . import RendererNotFoundError
from . import sysconfig
NAME_TO_RENDERER = {
"eni": eni,
+ "freebsd": freebsd,
"netplan": netplan,
"sysconfig": sysconfig,
}
-DEFAULT_PRIORITY = ["eni", "sysconfig", "netplan"]
+DEFAULT_PRIORITY = ["eni", "sysconfig", "netplan", "freebsd"]
def search(priority=None, target=None, first=False):
diff --git a/doc/rtd/topics/network-config.rst b/doc/rtd/topics/network-config.rst
index 1e99455..6620bcb 100644
--- a/doc/rtd/topics/network-config.rst
+++ b/doc/rtd/topics/network-config.rst
@@ -190,7 +190,7 @@ supplying an updated configuration in cloud-config. ::
system_info:
network:
- renderers: ['netplan', 'eni', 'sysconfig']
+ renderers: ['netplan', 'eni', 'sysconfig', 'freebsd']
Network Configuration Tools
diff --git a/tests/unittests/test_distros/test_netconfig.py b/tests/unittests/test_distros/test_netconfig.py
index c3c0c8c..7b29f71 100644
--- a/tests/unittests/test_distros/test_netconfig.py
+++ b/tests/unittests/test_distros/test_netconfig.py
@@ -1,5 +1,6 @@
# This file is part of cloud-init. See LICENSE file for license information.
+import copy
import os
from six import StringIO
from textwrap import dedent
@@ -213,128 +214,127 @@ class TestNetCfgDistroBase(FilesystemMockingTestCase):
self.assertEqual(v, b2[k])
-class TestNetCfgDistroFreebsd(TestNetCfgDistroBase):
+class TestNetCfgDistroFreeBSD(TestNetCfgDistroBase):
- frbsd_ifout = """\
-hn0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
+ def setUp(self):
+ super(TestNetCfgDistroFreeBSD, self).setUp()
+ self.distro = self._get_distro('freebsd', renderers=['freebsd'])
+
+ def _apply_and_verify_freebsd(self, apply_fn, config, expected_cfgs=None,
+ bringup=False):
+ if not expected_cfgs:
+ raise ValueError('expected_cfg must not be None')
+
+ tmpd = None
+ with mock.patch('cloudinit.net.freebsd.available') as m_avail:
+ m_avail.return_value = True
+ with self.reRooted(tmpd) as tmpd:
+ util.ensure_dir('/etc')
+ util.ensure_file('/etc/rc.conf')
+ util.ensure_file('/etc/resolv.conf')
+ apply_fn(config, bringup)
+
+ results = dir2dict(tmpd)
+ for cfgpath, expected in expected_cfgs.items():
+ print("----------")
+ print(expected)
+ print("^^^^ expected | rendered VVVVVVV")
+ print(results[cfgpath])
+ print("----------")
+ self.assertEqual(expected, results[cfgpath])
+ self.assertEqual(0o644, get_mode(cfgpath, tmpd))
+
+ @mock.patch('cloudinit.net.freebsd.Renderer._ifconfig_a')
+ def test_apply_network_config_freebsd_standard(self, ifconfig_a):
+ ifconfig_a.return_value = """\
+eth0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
options=51b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,TSO4,LRO>
ether 00:15:5d:4c:73:00
- inet6 fe80::215:5dff:fe4c:7300%hn0 prefixlen 64 scopeid 0x2
- inet 10.156.76.127 netmask 0xfffffc00 broadcast 10.156.79.255
- nd6 options=23<PERFORMNUD,ACCEPT_RTADV,AUTO_LINKLOCAL>
media: Ethernet autoselect (10Gbase-T <full-duplex>)
status: active
+
+eth1: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
+ options=6c07bb<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,JUMBO_MTU,VLAN_HWCSUM,TSO4,TSO6,LRO,VLAN_HWTSO,LINKSTATE,RXCSUM_IPV6,TXCSUM_IPV6>
+ ether 52:54:00:0e:33:89
+ media: Ethernet 10Gbase-T <full-duplex>
+ status: active
+
+lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> metric 0 mtu 16384
+ options=680003<RXCSUM,TXCSUM,LINKSTATE,RXCSUM_IPV6,TXCSUM_IPV6>
+ inet6 ::1 prefixlen 128
+ inet6 fe80::1%lo0 prefixlen 64 scopeid 0x2
+ inet 127.0.0.1 netmask 0xff000000
+ groups: lo
+ nd6 options=23<PERFORMNUD,ACCEPT_RTADV,AUTO_LINKLOCAL>
+"""
+ rc_conf_expected = """\
+defaultrouter="192.168.1.254"
+ifconfig_eth0="192.168.1.5 netmask 255.255.255.0"
+ifconfig_eth1="DHCP"
"""
- @mock.patch('cloudinit.distros.freebsd.Distro.get_ifconfig_list')
- @mock.patch('cloudinit.distros.freebsd.Distro.get_ifconfig_ifname_out')
- def test_get_ip_nic_freebsd(self, ifname_out, iflist):
- frbsd_distro = self._get_distro('freebsd')
- iflist.return_value = "lo0 hn0"
- ifname_out.return_value = self.frbsd_ifout
- res = frbsd_distro.get_ipv4()
- self.assertEqual(res, ['lo0', 'hn0'])
- res = frbsd_distro.get_ipv6()
- self.assertEqual(res, [])
-
- @mock.patch('cloudinit.distros.freebsd.Distro.get_ifconfig_ether')
- @mock.patch('cloudinit.distros.freebsd.Distro.get_ifconfig_ifname_out')
- @mock.patch('cloudinit.distros.freebsd.Distro.get_interface_mac')
- def test_generate_fallback_config_freebsd(self, mac, ifname_out, if_ether):
- frbsd_distro = self._get_distro('freebsd')
-
- if_ether.return_value = 'hn0'
- ifname_out.return_value = self.frbsd_ifout
- mac.return_value = '00:15:5d:4c:73:00'
- res = frbsd_distro.generate_fallback_config()
- self.assertIsNotNone(res)
-
- def test_simple_write_freebsd(self):
- fbsd_distro = self._get_distro('freebsd')
-
- rc_conf = '/etc/rc.conf'
- read_bufs = {
- rc_conf: 'initial-rc-conf-not-validated',
- '/etc/resolv.conf': 'initial-resolv-conf-not-validated',
+ expected_cfgs = {
+ '/etc/rc.conf': rc_conf_expected,
+ '/etc/resolv.conf': ''
}
+ self._apply_and_verify_freebsd(self.distro.apply_network_config,
+ V1_NET_CFG,
+ expected_cfgs=expected_cfgs.copy())
- tmpd = self.tmp_dir()
- populate_dir(tmpd, read_bufs)
- with self.reRooted(tmpd):
- with mock.patch("cloudinit.distros.freebsd.util.subp",
- return_value=('vtnet0', '')):
- fbsd_distro.apply_network(BASE_NET_CFG, False)
- results = dir2dict(tmpd)
-
- self.assertIn(rc_conf, results)
- self.assertCfgEquals(
- dedent('''\
- ifconfig_vtnet0="192.168.1.5 netmask 255.255.255.0"
- ifconfig_vtnet1="DHCP"
- defaultrouter="192.168.1.254"
- '''), results[rc_conf])
- self.assertEqual(0o644, get_mode(rc_conf, tmpd))
-
- def test_simple_write_freebsd_from_v2eni(self):
- fbsd_distro = self._get_distro('freebsd')
-
- rc_conf = '/etc/rc.conf'
- read_bufs = {
- rc_conf: 'initial-rc-conf-not-validated',
- '/etc/resolv.conf': 'initial-resolv-conf-not-validated',
- }
- tmpd = self.tmp_dir()
- populate_dir(tmpd, read_bufs)
- with self.reRooted(tmpd):
- with mock.patch("cloudinit.distros.freebsd.util.subp",
- return_value=('vtnet0', '')):
- fbsd_distro.apply_network(BASE_NET_CFG_FROM_V2, False)
- results = dir2dict(tmpd)
-
- self.assertIn(rc_conf, results)
- self.assertCfgEquals(
- dedent('''\
- ifconfig_vtnet0="192.168.1.5 netmask 255.255.255.0"
- ifconfig_vtnet1="DHCP"
- defaultrouter="192.168.1.254"
- '''), results[rc_conf])
- self.assertEqual(0o644, get_mode(rc_conf, tmpd))
-
- def test_apply_network_config_fallback_freebsd(self):
- fbsd_distro = self._get_distro('freebsd')
-
- # a weak attempt to verify that we don't have an implementation
- # of _write_network_config or apply_network_config in fbsd now,
- # which would make this test not actually test the fallback.
- self.assertRaises(
- NotImplementedError, fbsd_distro._write_network_config,
- BASE_NET_CFG)
-
- # now run
- mynetcfg = {
- 'config': [{"type": "physical", "name": "eth0",
- "mac_address": "c0:d6:9f:2c:e8:80",
- "subnets": [{"type": "dhcp"}]}],
- 'version': 1}
-
- rc_conf = '/etc/rc.conf'
- read_bufs = {
- rc_conf: 'initial-rc-conf-not-validated',
- '/etc/resolv.conf': 'initial-resolv-conf-not-validated',
+ @mock.patch('cloudinit.net.freebsd.Renderer._ifconfig_a')
+ def test_apply_network_config_freebsd_ifrename(self, ifconfig_a):
+ ifconfig_a.return_value = """\
+vtnet0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
+ options=51b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,TSO4,LRO>
+ ether 00:15:5d:4c:73:00
+ media: Ethernet autoselect (10Gbase-T <full-duplex>)
+ status: active
+
+lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> metric 0 mtu 16384
+ options=680003<RXCSUM,TXCSUM,LINKSTATE,RXCSUM_IPV6,TXCSUM_IPV6>
+ inet6 ::1 prefixlen 128
+ inet6 fe80::1%lo0 prefixlen 64 scopeid 0x2
+ inet 127.0.0.1 netmask 0xff000000
+ groups: lo
+ nd6 options=23<PERFORMNUD,ACCEPT_RTADV,AUTO_LINKLOCAL>
+"""
+ rc_conf_expected = """\
+ifconfig_vtnet0_name="eth0"
+defaultrouter="192.168.1.254"
+ifconfig_eth0="192.168.1.5 netmask 255.255.255.0"
+ifconfig_eth1="DHCP"
+"""
+
+ V1_NET_CFG_RENAME = copy.deepcopy(V1_NET_CFG)
+ V1_NET_CFG_RENAME['config'][0]['mac_address'] = '00:15:5d:4c:73:00'
+
+ expected_cfgs = {
+ '/etc/rc.conf': rc_conf_expected,
+ '/etc/resolv.conf': ''
}
+ self._apply_and_verify_freebsd(self.distro.apply_network_config,
+ V1_NET_CFG_RENAME,
+ expected_cfgs=expected_cfgs.copy())
- tmpd = self.tmp_dir()
- populate_dir(tmpd, read_bufs)
- with self.reRooted(tmpd):
- with mock.patch("cloudinit.distros.freebsd.util.subp",
- return_value=('vtnet0', '')):
- fbsd_distro.apply_network_config(mynetcfg, bring_up=False)
- results = dir2dict(tmpd)
-
- self.assertIn(rc_conf, results)
- self.assertCfgEquals('ifconfig_vtnet0="DHCP"', results[rc_conf])
- self.assertEqual(0o644, get_mode(rc_conf, tmpd))
+ @mock.patch('cloudinit.net.freebsd.Renderer._ifconfig_a')
+ def test_apply_network_config_freebsd_nameserver(self, ifconfig_a):
+ ifconfig_a.return_value = """\
+eth0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
+ options=51b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,TSO4,LRO>
+ ether 00:15:5d:4c:73:00
+ media: Ethernet autoselect (10Gbase-T <full-duplex>)
+ status: active
+"""
+
+ V1_NET_CFG_DNS = copy.deepcopy(V1_NET_CFG)
+ V1_NET_CFG_DNS['config'][0]['subnets'][0]['dns_nameservers'] = ['1.2.3.4']
+ expected_cfgs = {
+ '/etc/resolv.conf': 'nameserver 1.2.3.4\n'
+ }
+ self._apply_and_verify_freebsd(self.distro.apply_network_config,
+ V1_NET_CFG_DNS,
+ expected_cfgs=expected_cfgs.copy())
class TestNetCfgDistroUbuntuEni(TestNetCfgDistroBase):