From owner-svn-ports-all@freebsd.org Sun Dec 18 06:31:00 2016 Return-Path: Delivered-To: svn-ports-all@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id D66BFC867E9; Sun, 18 Dec 2016 06:31:00 +0000 (UTC) (envelope-from novel@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 73F853CD; Sun, 18 Dec 2016 06:31:00 +0000 (UTC) (envelope-from novel@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id uBI6Uxrw013834; Sun, 18 Dec 2016 06:30:59 GMT (envelope-from novel@FreeBSD.org) Received: (from novel@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id uBI6UwYP013550; Sun, 18 Dec 2016 06:30:58 GMT (envelope-from novel@FreeBSD.org) Message-Id: <201612180630.uBI6UwYP013550@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: novel set sender to novel@FreeBSD.org using -f From: Roman Bogorodskiy Date: Sun, 18 Dec 2016 06:30:58 +0000 (UTC) To: ports-committers@freebsd.org, svn-ports-all@freebsd.org, svn-ports-head@freebsd.org Subject: svn commit: r428837 - in head: . emulators emulators/py-nova emulators/py-nova/files X-SVN-Group: ports-head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-ports-all@freebsd.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: SVN commit messages for the ports tree List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 18 Dec 2016 06:31:00 -0000 Author: novel Date: Sun Dec 18 06:30:58 2016 New Revision: 428837 URL: https://svnweb.freebsd.org/changeset/ports/428837 Log: Add emulators/py-nova 14.0.2, Openstack Compute Service Please note that this is a development version of nova. Many features are not available. Currently nova works on FreeBSD 11 and supports QEMU and Xen. Common issues: - Security groups are not implemented - ARP spoofing, DHCP isolation protection are not implemented - Nova services work from the root user - No IPv6 support QEMU issues: - Need to enable serialconsole (TCP) - Need to disable online CPU tracking - Cannot mount cinder volumes Xen issues: - Live snapshots don't work - No support for cinder volume hot-plugging - XENBUS delay (5 min) when using qemu driver and COW images - Some Linux images cannot be booted For further FreeBSD specific notes please refer to port's pkg-message. PR: 215151 Submitted by: Alexander Nusov (alexander.nusov@nfvexpress.com) Added: head/emulators/py-nova/ head/emulators/py-nova/Makefile (contents, props changed) head/emulators/py-nova/distinfo (contents, props changed) head/emulators/py-nova/files/ head/emulators/py-nova/files/01-firewall-manager.patch (contents, props changed) head/emulators/py-nova/files/02-freebsd-l3.patch (contents, props changed) head/emulators/py-nova/files/03-nova-conf.patch (contents, props changed) head/emulators/py-nova/files/04-xen-phy.patch (contents, props changed) head/emulators/py-nova/files/05-online-cpus.patch (contents, props changed) head/emulators/py-nova/files/06-freebsd-net.patch (contents, props changed) head/emulators/py-nova/files/nova-api.in (contents, props changed) head/emulators/py-nova/files/nova-cells.in (contents, props changed) head/emulators/py-nova/files/nova-cert.in (contents, props changed) head/emulators/py-nova/files/nova-compute.conf.sample (contents, props changed) head/emulators/py-nova/files/nova-compute.in (contents, props changed) head/emulators/py-nova/files/nova-conductor.in (contents, props changed) head/emulators/py-nova/files/nova-consoleauth.in (contents, props changed) head/emulators/py-nova/files/nova-network.in (contents, props changed) head/emulators/py-nova/files/nova-scheduler.in (contents, props changed) head/emulators/py-nova/files/nova-serialproxy.in (contents, props changed) head/emulators/py-nova/files/nova.conf.sample (contents, props changed) head/emulators/py-nova/pkg-descr (contents, props changed) head/emulators/py-nova/pkg-message (contents, props changed) head/emulators/py-nova/pkg-plist (contents, props changed) Modified: head/GIDs head/UIDs head/emulators/Makefile Modified: head/GIDs ============================================================================== --- head/GIDs Sun Dec 18 06:10:44 2016 (r428836) +++ head/GIDs Sun Dec 18 06:30:58 2016 (r428837) @@ -137,7 +137,7 @@ moinmoin:*:192: cups:*:193: saned:*:194: radns:*:195: -# free: 196 +nova:*:196: # free: 197 # free: 198 mcserver:*:199: Modified: head/UIDs ============================================================================== --- head/UIDs Sun Dec 18 06:10:44 2016 (r428836) +++ head/UIDs Sun Dec 18 06:30:58 2016 (r428837) @@ -142,7 +142,7 @@ moinmoin:*:192:192::0:0:MoinMoin User:/n cups:*:193:193::0:0:Cups Owner:/nonexistent:/usr/sbin/nologin saned:*:194:194::0:0:SANE Scanner Daemon:/nonexistent:/bin/sh radns:*:195:195::0:0:radns user:/nonexistent:/usr/sbin/nologin -# free: 196 +nova:*:196:196::0:0:Nova daemon pseudo-user:/var/lib/nova:/usr/sbin/nologin # free: 197 # free: 198 mcserver:*:199:199::0:0:Minecraft Server:/nonexistent:/bin/sh Modified: head/emulators/Makefile ============================================================================== --- head/emulators/Makefile Sun Dec 18 06:10:44 2016 (r428836) +++ head/emulators/Makefile Sun Dec 18 06:30:58 2016 (r428837) @@ -115,6 +115,7 @@ SUBDIR += ppsspp-qt4 SUBDIR += ppsspp-qt5 SUBDIR += py-gns3-converter + SUBDIR += py-nova SUBDIR += q4wine SUBDIR += qemu SUBDIR += qemu-cheri Added: head/emulators/py-nova/Makefile ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/emulators/py-nova/Makefile Sun Dec 18 06:30:58 2016 (r428837) @@ -0,0 +1,141 @@ +# Created by: Alexander Nusov +# $FreeBSD$ + +PORTNAME= nova +PORTVERSION= 14.0.2 +CATEGORIES= emulators python +MASTER_SITES= https://fossies.org/linux/misc/openstack/ +PKGNAMEPREFIX= ${PYTHON_PKGNAMEPREFIX} + +MAINTAINER= alexander.nusov@nfvexpress.com +COMMENT= OpenStack compute service + +LICENSE= APACHE20 + +BUILD_DEPENDS= ${PYTHON_PKGNAMEPREFIX}pbr>=1.6:devel/py-pbr + +RUN_DEPENDS:= ${BUILD_DEPENDS} +RUN_DEPENDS+= ${PYTHON_PKGNAMEPREFIX}sqlalchemy10>=0:databases/py-sqlalchemy10 \ + ${PYTHON_PKGNAMEPREFIX}boto>=2.32.1:devel/py-boto \ + ${PYTHON_PKGNAMEPREFIX}decorator>=3.4.0:devel/py-decorator \ + ${PYTHON_PKGNAMEPREFIX}eventlet>=0:net/py-eventlet \ + ${PYTHON_PKGNAMEPREFIX}Jinja2>=2.8:devel/py-Jinja2 \ + ${PYTHON_PKGNAMEPREFIX}keystonemiddleware>=0:devel/py-keystonemiddleware \ + ${PYTHON_PKGNAMEPREFIX}lxml>=2.3:devel/py-lxml \ + ${PYTHON_PKGNAMEPREFIX}routes>=0:www/py-routes \ + ${PYTHON_PKGNAMEPREFIX}cryptography>=0:security/py-cryptography \ + ${PYTHON_PKGNAMEPREFIX}webob>=1.2.3:www/py-webob \ + ${PYTHON_PKGNAMEPREFIX}greenlet>=0.3.2:devel/py-greenlet \ + ${PYTHON_PKGNAMEPREFIX}PasteDeploy>=1.5.0:www/py-pastedeploy \ + ${PYTHON_PKGNAMEPREFIX}Paste>=0:www/py-paste \ + ${PYTHON_PKGNAMEPREFIX}prettytable>=0:devel/py-prettytable \ + ${PYTHON_PKGNAMEPREFIX}sqlalchemy-migrate>=0.9.6:databases/py-sqlalchemy-migrate \ + ${PYTHON_PKGNAMEPREFIX}netaddr>=0:net/py-netaddr \ + ${PYTHON_PKGNAMEPREFIX}netifaces>=0.10.4:net/py-netifaces \ + ${PYTHON_PKGNAMEPREFIX}paramiko>=2.0:security/py-paramiko \ + ${PYTHON_PKGNAMEPREFIX}Babel>=2.3.4:devel/py-babel \ + ${PYTHON_PKGNAMEPREFIX}iso8601>=0.1.11:devel/py-iso8601 \ + ${PYTHON_PKGNAMEPREFIX}jsonschema>=0:devel/py-jsonschema \ + ${PYTHON_PKGNAMEPREFIX}python-cinderclient>=0:net/py-python-cinderclient \ + ${PYTHON_PKGNAMEPREFIX}keystoneauth1>=2.10.0:devel/py-keystoneauth1 \ + ${PYTHON_PKGNAMEPREFIX}python-neutronclient>=5.1.0:net/py-python-neutronclient \ + ${PYTHON_PKGNAMEPREFIX}python-glanceclient>=0:net/py-python-glanceclient \ + ${PYTHON_PKGNAMEPREFIX}requests>=2.10.0:www/py-requests \ + ${PYTHON_PKGNAMEPREFIX}six>=1.9.0:devel/py-six \ + ${PYTHON_PKGNAMEPREFIX}stevedore>=1.16.0:devel/py-stevedore \ + ${PYTHON_PKGNAMEPREFIX}websockify>=0.8.0:devel/py-websockify \ + ${PYTHON_PKGNAMEPREFIX}oslo.cache>=1.5.0:devel/py-oslo.cache \ + ${PYTHON_PKGNAMEPREFIX}oslo.concurrency>=3.8.0:devel/py-oslo.concurrency \ + ${PYTHON_PKGNAMEPREFIX}oslo.config>=3.14.0:devel/py-oslo.config \ + ${PYTHON_PKGNAMEPREFIX}oslo.context>=2.9.0:devel/py-oslo.context \ + ${PYTHON_PKGNAMEPREFIX}oslo.log>=1.14.0:devel/py-oslo.log \ + ${PYTHON_PKGNAMEPREFIX}oslo.reports>=0.6.0:devel/py-oslo.reports \ + ${PYTHON_PKGNAMEPREFIX}oslo.serialization>=1.10.0:devel/py-oslo.serialization \ + ${PYTHON_PKGNAMEPREFIX}oslo.utils>=3.16.0:devel/py-oslo.utils \ + ${PYTHON_PKGNAMEPREFIX}oslo.db>=0:devel/py-oslo.db \ + ${PYTHON_PKGNAMEPREFIX}oslo.rootwrap>=5.0.0:devel/py-oslo.rootwrap \ + ${PYTHON_PKGNAMEPREFIX}oslo.messaging>=5.2.0:devel/py-oslo.messaging \ + ${PYTHON_PKGNAMEPREFIX}oslo.policy>=1.9.0:devel/py-oslo.policy \ + ${PYTHON_PKGNAMEPREFIX}oslo.privsep>=1.9.0:devel/py-oslo.privsep \ + ${PYTHON_PKGNAMEPREFIX}oslo.i18n>=2.1.0:devel/py-oslo.i18n \ + ${PYTHON_PKGNAMEPREFIX}oslo.service>=1.10.0:devel/py-oslo.service \ + ${PYTHON_PKGNAMEPREFIX}rfc3986>=0.2.2:www/py-rfc3986 \ + ${PYTHON_PKGNAMEPREFIX}oslo.middleware>=3.0.0:devel/py-oslo.middleware \ + ${PYTHON_PKGNAMEPREFIX}psutil121>=0:sysutils/py-psutil121 \ + ${PYTHON_PKGNAMEPREFIX}oslo.versionedobjects>=1.13.0:devel/py-oslo.versionedobjects \ + ${PYTHON_PKGNAMEPREFIX}os-brick>=1.6.1:devel/py-os-brick \ + ${PYTHON_PKGNAMEPREFIX}os-vif>=1.1.0:devel/py-os-vif \ + ${PYTHON_PKGNAMEPREFIX}os-win>=0.2.3:devel/py-os-win \ + ${PYTHON_PKGNAMEPREFIX}castellan>=0.4.0:devel/py-castellan \ + ${PYTHON_PKGNAMEPREFIX}microversion-parse>=0.1.2:devel/py-microversion-parse \ + ${PYTHON_PKGNAMEPREFIX}wsgi_intercept>=0.6.1:devel/py-wsgi_intercept \ + ${PYTHON_PKGNAMEPREFIX}sqlparse>=0:databases/py-sqlparse \ + ${PYTHON_PKGNAMEPREFIX}libvirt>=0:devel/py-libvirt \ + e2fsprogs>=0:sysutils/e2fsprogs \ + arping>=0:net/arping + +EXTRA_PATCHES= ${FILESDIR}/01-firewall-manager.patch:-p1 \ + ${FILESDIR}/02-freebsd-l3.patch:-p1 \ + ${FILESDIR}/03-nova-conf.patch:-p1 \ + ${FILESDIR}/04-xen-phy.patch:-p1 \ + ${FILESDIR}/05-online-cpus.patch:-p1 \ + ${FILESDIR}/06-freebsd-net.patch:-p1 + +ONLY_FOR_ARCHS= amd64 + +USES= python +USE_PYTHON= autoplist distutils noegginfo + +USE_RC_SUBR= nova-api \ + nova-consoleauth \ + nova-conductor \ + nova-scheduler \ + nova-network \ + nova-compute \ + nova-cert \ + nova-cells \ + nova-serialproxy +USERS= nova +GROUPS= nova + +.include + +.if ${OPSYS} != FreeBSD +IGNORE= only supported on FreeBSD +.endif + +.if ${OSVERSION} < 1100055 +IGNORE= only supported on recent FreeBSD 11 +.endif + +post-extract: + @(cd ${WRKSRC}/etc/nova; ${MV} logging_sample.conf logging.conf.sample) + @(cd ${WRKSRC}/etc/nova; for f in *.conf; do ${MV} $${f} $${f}.sample; done) + +post-patch: + ${CP} ${FILESDIR}/nova.conf.sample \ + ${WRKSRC}/etc/nova/nova.conf.sample + ${CP} ${FILESDIR}/nova-compute.conf.sample \ + ${WRKSRC}/etc/nova/nova-compute.conf.sample + ${REINPLACE_CMD} -e "s|/etc|${PREFIX}/etc|g" \ + ${WRKSRC}/nova/api/openstack/placement/wsgi.py \ + ${WRKSRC}/nova/conf/base.py \ + ${WRKSRC}/nova/conf/cloudpipe.py \ + ${WRKSRC}/nova/conf/network.py \ + ${WRKSRC}/nova/conf/remote_debug.py \ + ${WRKSRC}/nova/conf/xvp.py \ + ${WRKSRC}/nova/hacking/checks.py \ + ${WRKSRC}/nova/network/linux_net.py \ + ${WRKSRC}/nova/network/manager.py \ + ${WRKSRC}/nova/virt/disk/api.py \ + ${WRKSRC}/nova/virt/disk/vfs/guestfs.py \ + ${WRKSRC}/nova/virt/libvirt/driver.py \ + ${WRKSRC}/nova/virt/xenapi/agent.py \ + ${WRKSRC}/nova/wsgi/nova-api.py \ + ${WRKSRC}/nova/wsgi/nova-metadata.py + +post-install: + ${MKDIR} ${STAGEDIR}${ETCDIR} + ${CP} -R ${WRKSRC}/etc/nova/ ${STAGEDIR}${ETCDIR} + +.include Added: head/emulators/py-nova/distinfo ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/emulators/py-nova/distinfo Sun Dec 18 06:30:58 2016 (r428837) @@ -0,0 +1,3 @@ +TIMESTAMP = 1478994869 +SHA256 (nova-14.0.2.tar.gz) = e98291734b4b16615fb1518161c89749f09ad33b6344feb70d62b69e8161a50d +SIZE (nova-14.0.2.tar.gz) = 5574197 Added: head/emulators/py-nova/files/01-firewall-manager.patch ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/emulators/py-nova/files/01-firewall-manager.patch Sun Dec 18 06:30:58 2016 (r428837) @@ -0,0 +1,60 @@ +From 60668a502b5f7af77861507e94a89b7f4201c2cb Mon Sep 17 00:00:00 2001 +From: Alexander Nusov +Date: Tue, 8 Nov 2016 16:52:29 +0300 +Subject: [PATCH] add get_firewall_manager + +--- + nova/network/linux_net.py | 4 ++++ + nova/network/manager.py | 8 ++++---- + 2 files changed, 11 insertions(+), 4 deletions(-) + +diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py +index b10fa28..2637e26 100644 +--- a/nova/network/linux_net.py ++++ b/nova/network/linux_net.py +@@ -1898,3 +1898,7 @@ def set_vf_interface_vlan(pci_addr, mac_addr, vlan=0): + port_state, + run_as_root=True, + check_exit_code=exit_code) ++ ++ ++def get_firewall_manager(): ++ return iptables_manager +diff --git a/nova/network/manager.py b/nova/network/manager.py +index 9de53d9..f6eb106 100644 +--- a/nova/network/manager.py ++++ b/nova/network/manager.py +@@ -1799,13 +1799,13 @@ class FlatDHCPManager(RPCAllocateFixedIP, floating_ips.FloatingIP, + ctxt = context.get_admin_context() + networks = objects.NetworkList.get_by_host(ctxt, self.host) + +- self.driver.iptables_manager.defer_apply_on() ++ self.driver.get_firewall_manager().defer_apply_on() + + self.l3driver.initialize(fixed_range=False, networks=networks) + super(FlatDHCPManager, self).init_host() + self.init_host_floating_ips() + +- self.driver.iptables_manager.defer_apply_off() ++ self.driver.get_firewall_manager().defer_apply_off() + + def _setup_network_on_host(self, context, network): + """Sets up network on this host.""" +@@ -1887,13 +1887,13 @@ class VlanManager(RPCAllocateFixedIP, floating_ips.FloatingIP, NetworkManager): + ctxt = context.get_admin_context() + networks = objects.NetworkList.get_by_host(ctxt, self.host) + +- self.driver.iptables_manager.defer_apply_on() ++ self.driver.get_firewall_manager().defer_apply_on() + + self.l3driver.initialize(fixed_range=False, networks=networks) + NetworkManager.init_host(self) + self.init_host_floating_ips() + +- self.driver.iptables_manager.defer_apply_off() ++ self.driver.get_firewall_manager().defer_apply_off() + + def allocate_fixed_ip(self, context, instance_id, network, **kwargs): + """Gets a fixed IP from the pool.""" +-- +2.8.1 Added: head/emulators/py-nova/files/02-freebsd-l3.patch ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/emulators/py-nova/files/02-freebsd-l3.patch Sun Dec 18 06:30:58 2016 (r428837) @@ -0,0 +1,90 @@ +From 2e4a0e0a2588c4d52495fad8105aa1a4609797f6 Mon Sep 17 00:00:00 2001 +From: Alexander Nusov +Date: Tue, 8 Nov 2016 16:50:56 +0300 +Subject: [PATCH] add freebsd l3 driver + +--- + nova/network/l3.py | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 61 insertions(+) + +diff --git a/nova/network/l3.py b/nova/network/l3.py +index c13e6e2..a5982d5 100644 +--- a/nova/network/l3.py ++++ b/nova/network/l3.py +@@ -16,6 +16,7 @@ + from oslo_log import log as logging + + from nova.network import linux_net ++from nova.network import freebsd_net + from nova import utils + + LOG = logging.getLogger(__name__) +@@ -134,6 +135,66 @@ class LinuxNetL3(L3Driver): + pass + + ++class FreeBSDNetL3(L3Driver): ++ """L3 driver that uses freebsd_net as the backend.""" ++ def __init__(self): ++ self.initialized = False ++ ++ def initialize(self, **kwargs): ++ if self.initialized: ++ return ++ LOG.debug("Initializing freebsd_net L3 driver") ++ fixed_range = kwargs.get('fixed_range', False) ++ networks = kwargs.get('networks', None) ++ if not fixed_range and networks is not None: ++ for network in networks: ++ if network['enable_dhcp']: ++ is_ext = (network['dhcp_server'] is not None and ++ network['dhcp_server'] != network['gateway']) ++ self.initialize_network(network['cidr'], is_ext) ++ freebsd_net.ensure_metadata_ip() ++ freebsd_net.metadata_forward() ++ self.initialized = True ++ ++ def is_initialized(self): ++ return self.initialized ++ ++ def initialize_network(self, cidr, is_external): ++ freebsd_net.init_host(cidr, is_external) ++ ++ def initialize_gateway(self, network_ref): ++ mac_address = utils.generate_mac_address() ++ dev = freebsd_net.plug(network_ref, mac_address, ++ gateway=(network_ref['gateway'] is not None)) ++ freebsd_net.initialize_gateway_device(dev, network_ref) ++ ++ def remove_gateway(self, network_ref): ++ freebsd_net.unplug(network_ref) ++ ++ def add_floating_ip(self, floating_ip, fixed_ip, l3_interface_id, ++ network=None): ++ freebsd_net.ensure_floating_forward(floating_ip, fixed_ip, ++ l3_interface_id, network) ++ freebsd_net.bind_floating_ip(floating_ip, l3_interface_id) ++ ++ def remove_floating_ip(self, floating_ip, fixed_ip, l3_interface_id, ++ network=None): ++ freebsd_net.unbind_floating_ip(floating_ip, l3_interface_id) ++ freebsd_net.remove_floating_forward(floating_ip, fixed_ip, ++ l3_interface_id, network) ++ freebsd_net.clean_conntrack(fixed_ip) ++ ++ def add_vpn(self, public_ip, port, private_ip): ++ freebsd_net.ensure_vpn_forward(public_ip, port, private_ip) ++ ++ def remove_vpn(self, public_ip, port, private_ip): ++ # FreeBSD net currently doesn't implement any way of removing ++ # the VPN forwarding rules ++ pass ++ ++ def teardown(self): ++ pass ++ + class NullL3(L3Driver): + """The L3 driver that doesn't do anything. This class can be used when + nova-network should not manipulate L3 forwarding at all (e.g., in a Flat +-- +2.8.1 Added: head/emulators/py-nova/files/03-nova-conf.patch ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/emulators/py-nova/files/03-nova-conf.patch Sun Dec 18 06:30:58 2016 (r428837) @@ -0,0 +1,51 @@ +diff --git a/nova/conf/network.py b/nova/conf/network.py +index 3bb4cd9..eb57041 100644 +--- a/nova/conf/network.py ++++ b/nova/conf/network.py +@@ -704,6 +704,30 @@ Related options: + """), + ] + ++freebsd_net_opts = [ ++ cfg.StrOpt("freebsdnet_interface_driver", ++ default="nova.network.freebsd.FreeBSDBridgeInterfaceDriver", ++ help=""" ++This is the class used as the ethernet device driver for freebsdnet bridge ++operations. The default value should be all you need for most cases, but if you ++wish to use a customized class, set this option to the full dot-separated ++import path for that class. ++ ++Possible values: ++ ++ Any string representing a dot-separated class path that Nova can import. ++"""), ++ cfg.StrOpt("freebsdnet_ovs_integration_bridge", ++ default="br-int", ++ help=""" ++The name of the Open vSwitch bridge that is used with freebsdnet when connecting ++with Open vSwitch." ++ ++Possible values: ++ ++ Any string representing a valid bridge name. ++"""), ++] + + ldap_dns_opts = [ + cfg.StrOpt('ldap_dns_url', +@@ -766,12 +790,13 @@ by using this option. + 'some rpc network calls will be sent directly to host.'), + ] + +-ALL_DEFAULT_OPTS = (linux_net_opts + network_opts + ldap_dns_opts ++ALL_DEFAULT_OPTS = (linux_net_opts + freebsd_net_opts + network_opts + ldap_dns_opts + + rpcapi_opts + driver_opts) + + + def register_opts(conf): + conf.register_opts(linux_net_opts) ++ conf.register_opts(freebsd_net_opts) + conf.register_opts(network_opts) + conf.register_opts(ldap_dns_opts) + conf.register_opts(driver_opts) + Added: head/emulators/py-nova/files/04-xen-phy.patch ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/emulators/py-nova/files/04-xen-phy.patch Sun Dec 18 06:30:58 2016 (r428837) @@ -0,0 +1,42 @@ +From b4e9024ac90ed0eaf155bc24ef7ee3b01112366e Mon Sep 17 00:00:00 2001 +From: Alexander Nusov +Date: Tue, 15 Nov 2016 13:08:49 +0300 +Subject: [PATCH] foce xen phy option + +--- + nova/conf/libvirt.py | 3 +++ + nova/virt/libvirt/utils.py | 2 +- + 2 files changed, 4 insertions(+), 1 deletion(-) + +diff --git a/nova/conf/libvirt.py b/nova/conf/libvirt.py +index bfb278e..2eba080 100644 +--- a/nova/conf/libvirt.py ++++ b/nova/conf/libvirt.py +@@ -498,6 +498,9 @@ libvirt_imagebackend_opts = [ + help='Discard option for nova managed disks. Need' + ' Libvirt(1.0.6) Qemu1.5 (raw format) Qemu1.6(qcow2' + ' format)'), ++ cfg.BoolOpt('force_xen_phy', ++ default=False, ++ help='Force using of PHY driver in Xen'), + ] + + libvirt_imagecache_opts = [ +diff --git a/nova/virt/libvirt/utils.py b/nova/virt/libvirt/utils.py +index f0a4796..ff68d45 100644 +--- a/nova/virt/libvirt/utils.py ++++ b/nova/virt/libvirt/utils.py +@@ -112,7 +112,7 @@ def pick_disk_driver_name(hypervisor_version, is_block_dev=False): + :returns: driver_name or None + """ + if CONF.libvirt.virt_type == "xen": +- if is_block_dev: ++ if is_block_dev or CONF.libvirt.force_xen_phy: + return "phy" + else: + # 4002000 == 4.2.0 +-- +2.8.1 + + + Added: head/emulators/py-nova/files/05-online-cpus.patch ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/emulators/py-nova/files/05-online-cpus.patch Sun Dec 18 06:30:58 2016 (r428837) @@ -0,0 +1,63 @@ +From 8eb8fb92f21243ae0e41f2f626398d09582de29b Mon Sep 17 00:00:00 2001 +From: Alexander Nusov +Date: Fri, 25 Nov 2016 17:36:10 +0300 +Subject: [PATCH] add online cpu tracking option + +--- + nova/conf/libvirt.py | 3 +++ + nova/virt/libvirt/driver.py | 18 +++++++++++------- + 2 files changed, 14 insertions(+), 7 deletions(-) + +diff --git a/nova/conf/libvirt.py b/nova/conf/libvirt.py +index 2eba080..d133b03 100644 +--- a/nova/conf/libvirt.py ++++ b/nova/conf/libvirt.py +@@ -471,6 +471,9 @@ events`, refer https://libvirt.org/formatdomain.html#elementsPerf . + None + + """), ++ cfg.BoolOpt('online_cpu_tracking', ++ default=True, ++ help='Enable online cpu tracking'), + ] + + libvirt_imagebackend_opts = [ +diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py +index f9225de..f19ef70 100644 +--- a/nova/virt/libvirt/driver.py ++++ b/nova/virt/libvirt/driver.py +@@ -5188,11 +5188,12 @@ class LibvirtDriver(driver.ComputeDriver): + + cells = [] + allowed_cpus = hardware.get_vcpu_pin_set() +- online_cpus = self._host.get_online_cpus() +- if allowed_cpus: +- allowed_cpus &= online_cpus +- else: +- allowed_cpus = online_cpus ++ if CONF.libvirt.online_cpu_tracking: ++ online_cpus = self._host.get_online_cpus() ++ if allowed_cpus: ++ allowed_cpus &= online_cpus ++ else: ++ allowed_cpus = online_cpus + + def _get_reserved_memory_for_cell(self, cell_id, page_size): + cell = self._reserved_hugepages.get(cell_id, {}) +@@ -5205,8 +5206,11 @@ class LibvirtDriver(driver.ComputeDriver): + if cpu.siblings else () + for cpu in cell.cpus) + )) +- cpuset &= allowed_cpus +- siblings = [sib & allowed_cpus for sib in siblings] ++ if CONF.libvirt.online_cpu_tracking or allowed_cpus: ++ cpuset &= allowed_cpus ++ siblings = [sib & allowed_cpus for sib in siblings] ++ ++ + # Filter out singles and empty sibling sets that may be left + siblings = [sib for sib in siblings if len(sib) > 1] + +-- +2.8.1 + Added: head/emulators/py-nova/files/06-freebsd-net.patch ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/emulators/py-nova/files/06-freebsd-net.patch Sun Dec 18 06:30:58 2016 (r428837) @@ -0,0 +1,1245 @@ +From 2dd71331d4d204466e7b066f62952990e55c2e24 Mon Sep 17 00:00:00 2001 +From: Alexander Nusov +Date: Tue, 29 Nov 2016 14:21:41 +0300 +Subject: [PATCH] add freebsd_net driver + +--- + nova/network/freebsd_net.py | 1226 +++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 1226 insertions(+) + create mode 100644 nova/network/freebsd_net.py + +diff --git a/nova/network/freebsd_net.py b/nova/network/freebsd_net.py +new file mode 100644 +index 0000000..b71fcf6 +--- /dev/null ++++ b/nova/network/freebsd_net.py +@@ -0,0 +1,1226 @@ ++# Copyright (c) 2011 X.commerce, a business unit of eBay Inc. ++# Copyright 2010 United States Government as represented by the ++# Administrator of the National Aeronautics and Space Administration. ++# All Rights Reserved. ++# ++# 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. ++ ++"""Implements vlans, bridges, and iptables rules using linux utilities.""" ++ ++import calendar ++import inspect ++import os ++import re ++import time ++import json ++ ++import netaddr ++import netifaces ++import socket ++import struct ++ ++from oslo_concurrency import processutils ++from oslo_log import log as logging ++from oslo_serialization import jsonutils ++from oslo_utils import excutils ++from oslo_utils import fileutils ++from oslo_utils import importutils ++from oslo_utils import timeutils ++import six ++ ++import nova.conf ++from nova import exception ++from nova.i18n import _, _LE, _LW ++from nova.network import model as network_model ++from nova import objects ++from nova.pci import utils as pci_utils ++from nova import utils ++ ++LOG = logging.getLogger(__name__) ++ ++ ++CONF = nova.conf.CONF ++ ++ ++# NOTE(vish): Iptables supports chain names of up to 28 characters, and we ++# add up to 12 characters to binary_name which is used as a prefix, ++# so we limit it to 16 characters. ++# (max_chain_name_length - len('-POSTROUTING') == 16) ++def get_binary_name(): ++ """Grab the name of the binary we're running in.""" ++ return os.path.basename(inspect.stack()[-1][1])[:16] ++ ++binary_name = get_binary_name() ++ ++ ++# NOTE(jkoelker) This is just a nice little stub point since mocking ++# builtins with mox is a nightmare ++def write_to_file(file, data, mode='w'): ++ with open(file, mode) as f: ++ f.write(data) ++ ++ ++def is_pid_cmdline_correct(pid, match): ++ """Ensure that the cmdline for a pid seems sane ++ ++ Because pids are recycled, blindly killing by pid is something to ++ avoid. This provides the ability to include a substring that is ++ expected in the cmdline as a safety check. ++ """ ++ try: ++ with open('/proc/%d/cmdline' % pid) as f: ++ cmdline = f.read() ++ return match in cmdline ++ except EnvironmentError: ++ return False ++ ++ ++def metadata_forward(): ++ """Create forwarding rule for metadata.""" ++ firewall_manager.add_rule("rdr proto tcp from any to 169.254.169.254 " ++ "port 80 -> %s port %s" % ++ (CONF.metadata_host, CONF.metadata_port)) ++ firewall_manager.add_rule("pass out route-to (lo0 127.0.0.1) proto tcp " ++ "from any to 169.254.169.254 port 80") ++ firewall_manager.apply() ++ ++ ++def metadata_accept(): ++ """Create the filter accept rule for metadata.""" ++ firewall_manager.add_rule("pass in inet proto tcp from any to " ++ "169.254.169.254 port = http " ++ "flags S/SA keep state") ++ firewall_manager.apply() ++ ++ ++def init_host(ip_range, is_external=False): ++ """Basic networking setup goes here.""" ++ # NOTE(devcamcar): Cloud public SNAT entries and the default ++ # SNAT rule for outbound traffic. ++ ++ firewall_manager.add_snat_rule(ip_range, is_external) ++ if is_external: ++ for snat_range in CONF.force_snat_range: ++ firewall_manager.add_rule("pass quick inet from %s to %s" % ++ (ip_range, snat_range)) ++ firewall_manager.add_rule("pass quick inet from %s to %s/32" % ++ (ip_range, CONF.metadata_host)) ++ for dmz in CONF.dmz_cidr: ++ firewall_manager.add_rule("pass quick inet from %s to %s" % ++ (ip_range, dmz)) ++ ++ """ ++ iptables_manager.ipv4['nat'].add_rule('POSTROUTING', ++ '-s %(range)s -d %(range)s ' ++ '-m conntrack ! --ctstate DNAT ' ++ '-j ACCEPT' % ++ {'range': ip_range}) ++ """ ++ firewall_manager.apply() ++ ++ ++def send_arp_for_ip(ip, device, count): ++ out, err = _execute('arping', '-U', '-i', device, '-c', str(count), ip, ++ run_as_root=True, check_exit_code=False) ++ ++ if err: ++ LOG.debug('arping error for IP %s', ip) ++ ++ ++def bind_floating_ip(floating_ip, device): ++ """Bind IP to public interface.""" ++ _execute('ifconfig', device, str(floating_ip) + '/32', 'add', ++ run_as_root=True, check_exit_code=0) ++ ++ if CONF.send_arp_for_ha and CONF.send_arp_for_ha_count > 0: ++ send_arp_for_ip(floating_ip, device, CONF.send_arp_for_ha_count) ++ ++ ++def unbind_floating_ip(floating_ip, device): ++ """Unbind a public IP from public interface.""" ++ _execute('ifconfig', device, str(floating_ip) + '/32', 'delete', ++ run_as_root=True, check_exit_code=0) ++ ++ ++def ensure_metadata_ip(): ++ """Sets up local metadata IP.""" ++ _execute('ifconfig', 'lo0', 'alias', '169.254.169.254/32', ++ run_as_root=True, check_exit_code=0) ++ ++ ++def ensure_vpn_forward(public_ip, port, private_ip): ++ """Sets up forwarding rules for vlan.""" ++ firewall_manager.add_rule("pass in proto udp " ++ "from any to %s port 1194 " % ++ (private_ip)) ++ firewall_manager.add_rule("rdr proto udp from any to %s port %s -> " ++ "%s port 1194" % ++ (public_ip, port, private_ip)) ++ firewall_manager.apply() ++ ++ ++def ensure_floating_forward(floating_ip, fixed_ip, device, network): ++ """Ensure floating IP forwarding rule.""" ++ firewall_manager.ensure_floating_rules(floating_ip, fixed_ip, device) ++ if device != network['bridge']: ++ firewall_manager.ensure_in_network_traffic_rules(fixed_ip, network) ++ firewall_manager.apply() ++ ++ ++def remove_floating_forward(floating_ip, fixed_ip, device, network): ++ """Remove forwarding for floating IP.""" ++ firewall_manager.remove_floating_rules(floating_ip, fixed_ip, device) ++ if device != network['bridge']: ++ firewall_manager.remove_in_network_traffic_rules(fixed_ip, network) ++ firewall_manager.apply() ++ ++ ++def clean_conntrack(fixed_ip): ++ pass ++ ++ ++def _enable_ipv4_forwarding(): ++ sysctl_key = 'net.inet.ip.forwarding' ++ stdout, stderr = _execute('sysctl', '-n', sysctl_key) ++ if stdout.strip() is not '1': ++ _execute('sysctl', '%s=1' % sysctl_key, run_as_root=True) ++ ++ ++@utils.synchronized('lock_gateway', external=True) ++def initialize_gateway_device(dev, network_ref): ++ if not network_ref: ++ return ++ ++ _enable_ipv4_forwarding() ++ ++ # NOTE(vish): The ip for dnsmasq has to be the first address on the ++ # bridge for it to respond to requests properly ++ try: ++ prefix = network_ref.cidr.prefixlen ++ except AttributeError: ++ prefix = network_ref['cidr'].rpartition('/')[2] ++ ++ full_ip = '%s/%s' % (network_ref['dhcp_server'], prefix) ++ new_ip_params = [['inet', full_ip, 'broadcast', network_ref['broadcast']]] ++ old_ip_params = [] ++ out, err = _execute('ifconfig', dev) ++ for line in out.split('\n'): ++ fields = line.split() ++ if fields and fields[0] == 'inet': ++ old_ip_params.append(fields) ++ if _address_to_cidr(fields[1], fields[3]) != full_ip: ++ new_ip_params.append(fields) ++ if not old_ip_params or _address_to_cidr(old_ip_params[0][1], old_ip_params[0][3]) != full_ip: ++ old_routes = [] ++ result = _execute('netstat', '-nrW', '-f', 'inet') ++ if result: ++ out, err = result ++ for line in out.split('\n'): ++ fields = line.split() ++ if len(fields) > 6 and (fields[6] == dev) and ('G' in fields[2]): ++ old_routes.append(fields) ++ _execute('route', '-q', 'delete', fields[0], fields[1], ++ run_as_root=True) ++ for ip_params in old_ip_params: ++ _execute(*_ifconfig_tail_cmd(dev, ip_params, 'delete'), ++ run_as_root=True) ++ for ip_params in new_ip_params: ++ _execute(*_ifconfig_tail_cmd(dev, ip_params, 'add'), ++ run_as_root=True) ++ ++ for fields in old_routes: ++ _execute('route', '-q', 'add', fields[0], fields[1], ++ run_as_root=True) ++ if CONF.send_arp_for_ha and CONF.send_arp_for_ha_count > 0: ++ send_arp_for_ip(network_ref['dhcp_server'], dev, ++ CONF.send_arp_for_ha_count) ++ if CONF.use_ipv6: ++ _execute('ifconfig', dev, 'inet6', network_ref['cidr_v6'], ++ run_as_root=True) ++ ++ ++def get_dhcp_leases(context, network_ref): ++ """Return a network's hosts config in dnsmasq leasefile format.""" ++ hosts = [] ++ host = None ++ if network_ref['multi_host']: ++ host = CONF.host ++ for fixedip in objects.FixedIPList.get_by_network(context, ++ network_ref, ++ host=host): ++ # NOTE(cfb): Don't return a lease entry if the IP isn't ++ # already leased ++ if fixedip.leased: ++ hosts.append(_host_lease(fixedip)) ++ ++ return '\n'.join(hosts) ++ ++ ++def get_dhcp_hosts(context, network_ref, fixedips): ++ """Get network's hosts config in dhcp-host format.""" ++ hosts = [] ++ macs = set() ++ for fixedip in fixedips: ++ if fixedip.allocated: ++ if fixedip.virtual_interface.address not in macs: ++ hosts.append(_host_dhcp(fixedip)) ++ macs.add(fixedip.virtual_interface.address) ++ return '\n'.join(hosts) ++ ++ ++def get_dns_hosts(context, network_ref): ++ """Get network's DNS hosts in hosts format.""" ++ hosts = [] ++ for fixedip in objects.FixedIPList.get_by_network(context, network_ref): ++ if fixedip.allocated: ++ hosts.append(_host_dns(fixedip)) ++ return '\n'.join(hosts) ++ ++ ++def _add_dnsmasq_accept_rules(dev): ++ """Allow DHCP and DNS traffic through to dnsmasq.""" ++ for port in [67, 53]: ++ for proto in ['udp', 'tcp']: ++ firewall_manager.add_rule("pass in on %s inet proto %s " ++ "from any to any port %s" % ++ (dev, proto, port)) ++ firewall_manager.apply() ++ ++ ++def _remove_dnsmasq_accept_rules(dev): ++ """Remove DHCP and DNS traffic allowed through to dnsmasq.""" ++ for port in [67, 53]: ++ for proto in ['udp', 'tcp']: ++ firewall_manager.remove_rule("pass in on %s inet proto %s " ++ "from any to any port %s" % ++ (dev, proto, port)) ++ firewall_manager.apply() ++ ++ ++def get_dhcp_opts(context, network_ref, fixedips): ++ """Get network's hosts config in dhcp-opts format.""" ++ gateway = network_ref['gateway'] ++ # NOTE(vish): if we are in multi-host mode and we are not sharing ++ # addresses, then we actually need to hand out the ++ # dhcp server address as the gateway. ++ if network_ref['multi_host'] and not (network_ref['share_address'] or ++ CONF.share_dhcp_address): ++ gateway = network_ref['dhcp_server'] ++ hosts = [] ++ if CONF.use_single_default_gateway: ++ for fixedip in fixedips: ++ if fixedip.allocated: ++ vif_id = fixedip.virtual_interface_id ++ if fixedip.default_route: ++ hosts.append(_host_dhcp_opts(vif_id, gateway)) ++ else: ++ hosts.append(_host_dhcp_opts(vif_id)) ++ else: ++ hosts.append(_host_dhcp_opts(None, gateway)) ++ return '\n'.join(hosts) ++ ++ ++def release_dhcp(dev, address, mac_address): ++ if device_exists(dev): ++ try: ++ utils.execute('dhcp_release', dev, address, mac_address, ++ run_as_root=True) ++ except processutils.ProcessExecutionError: ++ raise exception.NetworkDhcpReleaseFailed(address=address, ++ mac_address=mac_address) ++ ++ ++def update_dhcp(context, dev, network_ref): ++ conffile = _dhcp_file(dev, 'conf') ++ host = None ++ if network_ref['multi_host']: ++ host = CONF.host ++ fixedips = objects.FixedIPList.get_by_network(context, ++ network_ref, ++ host=host) ++ write_to_file(conffile, get_dhcp_hosts(context, network_ref, fixedips)) ++ restart_dhcp(context, dev, network_ref, fixedips) ++ ++ ++def update_dns(context, dev, network_ref): ++ hostsfile = _dhcp_file(dev, 'hosts') ++ host = None ++ if network_ref['multi_host']: ++ host = CONF.host ++ fixedips = objects.FixedIPList.get_by_network(context, ++ network_ref, ++ host=host) ++ write_to_file(hostsfile, get_dns_hosts(context, network_ref)) ++ restart_dhcp(context, dev, network_ref, fixedips) ++ ++ ++def kill_dhcp(dev): ++ pid = _dnsmasq_pid_for(dev) ++ if pid: ++ # Check that the process exists and looks like a dnsmasq process ++ conffile = _dhcp_file(dev, 'conf') ++ if is_pid_cmdline_correct(pid, conffile.split('/')[-1]): ++ _execute('kill', '-9', pid, run_as_root=True) ++ else: ++ LOG.debug('Pid %d is stale, skip killing dnsmasq', pid) ++ _remove_dnsmasq_accept_rules(dev) ++ ++ ++# NOTE(ja): Sending a HUP only reloads the hostfile, so any ++# configuration options (like dchp-range, vlan, ...) ++# aren't reloaded. ++@utils.synchronized('dnsmasq_start') ++def restart_dhcp(context, dev, network_ref, fixedips): ++ """(Re)starts a dnsmasq server for a given network. ++ ++ If a dnsmasq instance is already running then send a HUP ++ signal causing it to reload, otherwise spawn a new instance. ++ ++ """ ++ conffile = _dhcp_file(dev, 'conf') ++ ++ optsfile = _dhcp_file(dev, 'opts') ++ write_to_file(optsfile, get_dhcp_opts(context, network_ref, fixedips)) ++ os.chmod(optsfile, 0o644) ++ ++ # Make sure dnsmasq can actually read it (it setuid()s to "nobody") ++ os.chmod(conffile, 0o644) ++ ++ pid = _dnsmasq_pid_for(dev) ++ ++ # if dnsmasq is already running, then tell it to reload ++ if pid: ++ if is_pid_cmdline_correct(pid, conffile.split('/')[-1]): ++ try: ++ _execute('kill', '-HUP', pid, run_as_root=True) ++ _add_dnsmasq_accept_rules(dev) ++ return ++ except Exception as exc: ++ LOG.error(_LE('kill -HUP dnsmasq threw %s'), exc) ++ else: ++ LOG.debug('Pid %d is stale, relaunching dnsmasq', pid) ++ ++ cmd = ['env', ++ 'CONFIG_FILE=%s' % jsonutils.dumps(CONF.dhcpbridge_flagfile), ++ 'NETWORK_ID=%s' % str(network_ref['id']), ++ 'dnsmasq', ++ '--strict-order', ++ '--bind-interfaces', ++ '--conf-file=%s' % CONF.dnsmasq_config_file, ++ '--pid-file=%s' % _dhcp_file(dev, 'pid'), ++ '--dhcp-optsfile=%s' % _dhcp_file(dev, 'opts'), ++ '--listen-address=%s' % network_ref['dhcp_server'], ++ '--except-interface=lo', ++ '--dhcp-range=set:%s,%s,static,%s,%ss' % ++ (network_ref['label'], ++ network_ref['dhcp_start'], ++ network_ref['netmask'], ++ CONF.dhcp_lease_time), ++ '--dhcp-lease-max=%s' % len(netaddr.IPNetwork(network_ref['cidr'])), ++ '--dhcp-hostsfile=%s' % _dhcp_file(dev, 'conf'), ++ '--dhcp-script=%s' % CONF.dhcpbridge, ++ '--no-hosts', ++ '--leasefile-ro'] ++ ++ # dnsmasq currently gives an error for an empty domain, ++ # rather than ignoring. So only specify it if defined. ++ if CONF.dhcp_domain: ++ cmd.append('--domain=%s' % CONF.dhcp_domain) ++ ++ dns_servers = CONF.dns_server ++ if CONF.use_network_dns_servers: ++ if network_ref.get('dns1'): ++ dns_servers.append(network_ref.get('dns1')) ++ if network_ref.get('dns2'): ++ dns_servers.append(network_ref.get('dns2')) ++ if network_ref['multi_host']: ++ cmd.append('--addn-hosts=%s' % _dhcp_file(dev, 'hosts')) ++ if dns_servers: ++ cmd.append('--no-resolv') ++ for dns_server in dns_servers: ++ cmd.append('--server=%s' % dns_server) ++ ++ _execute(*cmd, run_as_root=True) ++ ++ _add_dnsmasq_accept_rules(dev) ++ ++ ++@utils.synchronized('radvd_start') ++def update_ra(context, dev, network_ref): ++ conffile = _ra_file(dev, 'conf') ++ conf_str = """ ++interface %s ++{ ++ AdvSendAdvert on; ++ MinRtrAdvInterval 3; ++ MaxRtrAdvInterval 10; ++ prefix %s ++ { ++ AdvOnLink on; ++ AdvAutonomous on; ++ }; ++}; ++""" % (dev, network_ref['cidr_v6']) ++ write_to_file(conffile, conf_str) ++ ++ # Make sure radvd can actually read it (it setuid()s to "nobody") ++ os.chmod(conffile, 0o644) ++ *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***