PSARC/2016/251 OpenStack Neutron ML2 framework and OVS mechanism driver
22600422 Add support for OVS in OpenStack Neutron
--- a/components/openstack/common/files/openstack_common.py Thu May 19 13:53:26 2016 -0700
+++ b/components/openstack/common/files/openstack_common.py Thu May 19 14:00:51 2016 -0700
@@ -1,4 +1,4 @@
-# Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2015, 2016, Oracle and/or its affiliates. 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
@@ -16,19 +16,29 @@
""" openstack_upgrade - common functions used by the various OpenStack
components to facilitate upgrading of configuration files and MySQL
databases/tables (if in use)
+
+Wrapper functions around ovs-vsctl(8) command used by Nova and Neutron to
+determine the lower-link needed for VNIC creation.
"""
+import collections
from ConfigParser import NoOptionError
from datetime import datetime
import errno
import glob
+import json
import os
import shutil
+from subprocess import Popen, PIPE
import time
+import uuid
import iniparse
+OVS_VSCTL_TIMEOUT = "10"
+
+
def create_backups(directory):
""" create backups of each configuration file which also has a .new file
from the upgrade.
@@ -230,3 +240,53 @@
with open(new_file, 'wb+') as fh:
new.write(fh)
+
+
+def _val_to_py(val):
+ """Convert a json ovsdb return value to native python object"""
+ if isinstance(val, collections.Sequence) and len(val) == 2:
+ if val[0] == "uuid":
+ return uuid.UUID(val[1])
+ elif val[0] == "set":
+ return [_val_to_py(x) for x in val[1]]
+ elif val[0] == "map":
+ return {_val_to_py(x): _val_to_py(y) for x, y in val[1]}
+ return val
+
+
+def get_ovsdb_info(table, columns=None):
+ """Return a list of dictionaries where-in each dictionary captures the
+ requested (or all) column name and its value"""
+ cmd = ["/usr/sbin/ovs-vsctl", "-t", OVS_VSCTL_TIMEOUT, "-f", "json"]
+ if columns:
+ cmd.extend(["--columns", ",".join(columns)])
+ cmd.append("list")
+ cmd.append(table)
+ p = Popen(cmd, stdout=PIPE, stderr=PIPE)
+ output, error = p.communicate()
+ if p.returncode != 0:
+ raise RuntimeError("Error running '%s': %s" % (" ".join(cmd), error))
+ output = output.strip().replace(r'\\', '\\')
+ try:
+ ovsinfo = json.loads(output)
+ except Exception as err:
+ raise RuntimeError("Failed to load %s as json: %s" % (output, err))
+ headings = ovsinfo['headings']
+ data = ovsinfo['data']
+ results = []
+ for record in data:
+ obj = {}
+ for pos, heading in enumerate(headings):
+ obj[heading] = _val_to_py(record[pos])
+ results.append(obj)
+ return results
+
+
+def is_ml2_plugin():
+ parser = iniparse.ConfigParser()
+ parser.readfp(open("/etc/neutron/neutron.conf"))
+ try:
+ core_plugin = parser.get("DEFAULT", "core_plugin")
+ except NoOptionError:
+ return False
+ return "ml2" in core_plugin.lower()
--- a/components/openstack/neutron/Makefile Thu May 19 13:53:26 2016 -0700
+++ b/components/openstack/neutron/Makefile Thu May 19 14:00:51 2016 -0700
@@ -90,6 +90,7 @@
files/neutron-dhcp-agent.xml \
files/neutron-l3-agent.xml \
files/neutron-metadata-agent.xml \
+ files/neutron-openvswitch-agent.xml \
files/neutron-server.xml \
files/neutron-upgrade.xml \
$(PROTO_DIR)/lib/svc/manifest/application/openstack; \
@@ -145,14 +146,17 @@
REQUIRED_PACKAGES += library/python/eventlet-27
REQUIRED_PACKAGES += library/python/iniparse-27
REQUIRED_PACKAGES += library/python/netaddr-27
+REQUIRED_PACKAGES += library/python/netifaces-27
REQUIRED_PACKAGES += library/python/oslo.config-27
REQUIRED_PACKAGES += library/python/oslo.db-27
REQUIRED_PACKAGES += library/python/simplejson-27
REQUIRED_PACKAGES += library/python/six-27
REQUIRED_PACKAGES += library/python/sqlalchemy-27
+REQUIRED_PACKAGES += network/arping
REQUIRED_PACKAGES += network/firewall
REQUIRED_PACKAGES += service/network/dnsmasq
REQUIRED_PACKAGES += service/network/evs
+REQUIRED_PACKAGES += service/network/openvswitch
REQUIRED_PACKAGES += system/core-os
REQUIRED_PACKAGES += system/management/rad/client/rad-python
REQUIRED_PACKAGES += system/network
--- a/components/openstack/neutron/files/agent/evs_l3_agent.py Thu May 19 13:53:26 2016 -0700
+++ b/components/openstack/neutron/files/agent/evs_l3_agent.py Thu May 19 14:00:51 2016 -0700
@@ -27,6 +27,7 @@
from oslo.config import cfg
from oslo_log import log as logging
+from neutron.agent.common import ovs_lib
from neutron.agent.l3 import agent as l3_agent
from neutron.agent.l3 import router_info as router
from neutron.agent.linux import utils
@@ -102,9 +103,14 @@
internal_dlname = self.get_internal_device_name(port['id'])
# driver just returns if datalink and IP interface already exists
self.driver.plug(port['tenant_id'], port['network_id'], port['id'],
- internal_dlname)
- ip_cidrs = common_utils.fixed_ip_cidrs(port['fixed_ips'])
+ internal_dlname, port['mac_address'])
+ fixed_ips = port['fixed_ips']
+ ip_cidrs = common_utils.fixed_ip_cidrs(fixed_ips)
self.driver.init_l3(internal_dlname, ip_cidrs)
+ for fixed_ip in fixed_ips:
+ net_lib.send_ip_addr_adv_notif(internal_dlname,
+ fixed_ip['ip_address'],
+ self.agent_conf)
# Since we support shared router model, we need to block the new
# internal port from reaching other tenant's ports. However, if
@@ -318,6 +324,9 @@
ipintf.create_address(fip_cidr)
self.pf.add_rules([binat_rule], [interface_name,
fip_cidr.split('/')[0]])
+ net_lib.send_ip_addr_adv_notif(interface_name,
+ fip['floating_ip_address'],
+ self.agent_conf)
except Exception as err:
# any exception occurred here should cause the floating IP
# to be set in error state
@@ -346,47 +355,18 @@
ipintf.delete_address(ip_cidr)
return fip_statuses
- # Todo(gmoodalb): need to do more work on ipv6 gateway
+ # TODO(gmoodalb): need to do more work on ipv6 gateway
def external_gateway_added(self, ex_gw_port, external_dlname):
-
- if not net_lib.Datalink.datalink_exists(external_dlname):
- dl = net_lib.Datalink(external_dlname)
- # determine the network type of the external network
- evsname = ex_gw_port['network_id']
- cmd = ['/usr/sbin/evsadm', 'show-evs', '-co', 'l2type,vid',
- '-f', 'evs=%s' % evsname]
- try:
- stdout = utils.execute(cmd)
- except Exception as err:
- LOG.error(_("Failed to retrieve the network type for "
- "the external network, and it is required "
- "to create an external gateway port: %s") % err)
- return
- output = stdout.splitlines()[0].strip()
- l2type, vid = output.split(':')
- if l2type != 'flat' and l2type != 'vlan':
- LOG.error(_("External network should be either Flat or "
- "VLAN based, and it is required to "
- "create an external gateway port"))
- return
- elif (l2type == 'vlan' and
- self.agent_conf.get("external_network_datalink", None)):
- LOG.warning(_("external_network_datalink is deprecated in "
- "Juno and will be removed in the next release "
- "of Solaris OpenStack. Please use the evsadm "
- "set-controlprop subcommand to setup the "
- "uplink-port for an external network"))
- # proceed with the old-style of doing things
- mac_address = ex_gw_port['mac_address']
- dl.create_vnic(self.agent_conf.external_network_datalink,
- mac_address=mac_address, vid=vid)
- else:
- self.driver.plug(ex_gw_port['tenant_id'],
- ex_gw_port['network_id'],
- ex_gw_port['id'], external_dlname)
-
+ self.driver.plug(ex_gw_port['tenant_id'], ex_gw_port['network_id'],
+ ex_gw_port['id'], external_dlname,
+ ex_gw_port['mac_address'],
+ bridge=self.agent_conf.external_network_bridge)
ip_cidrs = common_utils.fixed_ip_cidrs(ex_gw_port['fixed_ips'])
self.driver.init_l3(external_dlname, ip_cidrs)
+ for fixed_ip in ex_gw_port['fixed_ips']:
+ net_lib.send_ip_addr_adv_notif(external_dlname,
+ fixed_ip['ip_address'],
+ self.agent_conf)
# add nested anchor rule first
anchor_option = "on %s" % external_dlname
@@ -439,7 +419,8 @@
if net_lib.Datalink.datalink_exists(external_dlname):
self.driver.fini_l3(external_dlname)
- self.driver.unplug(external_dlname)
+ self.driver.unplug(external_dlname,
+ self.agent_conf.external_network_bridge)
def _process_external_gateway(self, ex_gw_port):
# TODO(Carl) Refactor to clarify roles of ex_gw_port vs self.ex_gw_port
@@ -577,3 +558,35 @@
self.router_info[router_id] = ri
ri.initialize(self.process_monitor)
+
+ def _process_router_if_compatible(self, router):
+ if (self.conf.external_network_bridge and not ovs_lib.BaseOVS().
+ bridge_exists(self.conf.external_network_bridge)):
+ LOG.error(_("The external network bridge '%s' does not exist"),
+ self.conf.external_network_bridge)
+ return
+
+ # If namespaces are disabled, only process the router associated
+ # with the configured agent id.
+ if (not self.conf.use_namespaces and
+ router['id'] != self.conf.router_id):
+ raise n_exc.RouterNotCompatibleWithAgent(router_id=router['id'])
+
+ # Either ex_net_id or handle_internal_only_routers must be set
+ ex_net_id = (router['external_gateway_info'] or {}).get('network_id')
+ if not ex_net_id and not self.conf.handle_internal_only_routers:
+ raise n_exc.RouterNotCompatibleWithAgent(router_id=router['id'])
+
+ # If target_ex_net_id and ex_net_id are set they must be equal
+ target_ex_net_id = self._fetch_external_net_id()
+ if (target_ex_net_id and ex_net_id and ex_net_id != target_ex_net_id):
+ # Double check that our single external_net_id has not changed
+ # by forcing a check by RPC.
+ if ex_net_id != self._fetch_external_net_id(force=True):
+ raise n_exc.RouterNotCompatibleWithAgent(
+ router_id=router['id'])
+
+ if router['id'] not in self.router_info:
+ self._process_added_router(router)
+ else:
+ self._process_updated_router(router)
--- a/components/openstack/neutron/files/agent/solaris/dhcp.py Thu May 19 13:53:26 2016 -0700
+++ b/components/openstack/neutron/files/agent/solaris/dhcp.py Thu May 19 14:00:51 2016 -0700
@@ -3,7 +3,7 @@
# Copyright 2012 OpenStack Foundation
# All Rights Reserved.
#
-# Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2013, 2016, Oracle and/or its affiliates. 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
@@ -254,7 +254,8 @@
LOG.debug('Reusing existing device: %s.', interface_name)
else:
self.driver.plug(network.tenant_id, network.id,
- port.id, interface_name)
+ port.id, interface_name, port.mac_address,
+ network=network)
ip_cidrs = []
addrconf = False
for fixed_ip in port.fixed_ips:
--- a/components/openstack/neutron/files/agent/solaris/interface.py Thu May 19 13:53:26 2016 -0700
+++ b/components/openstack/neutron/files/agent/solaris/interface.py Thu May 19 14:00:51 2016 -0700
@@ -1,4 +1,4 @@
-# Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2013, 2016, Oracle and/or its affiliates. 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
@@ -14,17 +14,20 @@
#
# @author: Girish Moodalbail, Oracle, Inc.
-
+from openstack_common import get_ovsdb_info
import rad.client as radcli
import rad.connect as radcon
import rad.bindings.com.oracle.solaris.rad.evscntl_1 as evsbind
from oslo_config import cfg
from oslo_log import log as logging
+from oslo_serialization import jsonutils
+from neutron.agent.common import ovs_lib
from neutron.agent.linux import utils
from neutron.agent.solaris import net_lib
from neutron.common import exceptions
+from neutron.plugins.common import constants as p_const
LOG = logging.getLogger(__name__)
@@ -115,10 +118,49 @@
vnicname += self.VNIC_NAME_SUFFIX
return vnicname.replace('-', '_')
- def plug(self, tenant_id, network_id, port_id, datalink_name,
- namespace=None, prefix=None, protection=False):
+ def plug(self, tenant_id, network_id, port_id, datalink_name, mac_address,
+ network=None, bridge=None, namespace=None, prefix=None,
+ protection=False):
"""Plug in the interface."""
+ if net_lib.Datalink.datalink_exists(datalink_name):
+ LOG.info(_("Device %s already exists"), datalink_name)
+ return
+
+ if datalink_name.startswith('l3e'):
+ # verify external network parameter settings
+ dl = net_lib.Datalink(datalink_name)
+ # determine the network type of the external network
+ # TODO(gmoodalb): use EVS RAD APIs
+ evsname = network_id
+ cmd = ['/usr/sbin/evsadm', 'show-evs', '-co', 'l2type,vid',
+ '-f', 'evs=%s' % evsname]
+ try:
+ stdout = utils.execute(cmd)
+ except Exception as err:
+ LOG.error(_("Failed to retrieve the network type for "
+ "the external network, and it is required "
+ "to create an external gateway port: %s") % err)
+ return
+ output = stdout.splitlines()[0].strip()
+ l2type, vid = output.split(':')
+ if l2type != 'flat' and l2type != 'vlan':
+ LOG.error(_("External network should be either Flat or "
+ "VLAN based, and it is required to "
+ "create an external gateway port"))
+ return
+ elif (l2type == 'vlan' and
+ self.conf.get("external_network_datalink", None)):
+ LOG.warning(_("external_network_datalink is deprecated in "
+ "Juno and will be removed in the next release "
+ "of Solaris OpenStack. Please use the evsadm "
+ "set-controlprop subcommand to setup the "
+ "uplink-port for an external network"))
+ # proceed with the old-style of doing things
+ dl.create_vnic(self.conf.external_network_datalink,
+ mac_address=mac_address, vid=vid)
+ return
+
try:
evsc = self.rad_connection.get_object(evsbind.EVSController())
vports_info = evsc.getVPortInfo("vport=%s" % (port_id))
@@ -162,3 +204,129 @@
dl = net_lib.Datalink(device_name)
dl.delete_vnic()
+
+
+class OVSInterfaceDriver(SolarisVNICDriver):
+ """Driver used to manage Solaris OVS VNICs.
+
+ This class provides methods to create/delete a Crossbow VNIC and
+ add it as a port of OVS bridge.
+ """
+
+ def __init__(self, conf):
+ self.conf = conf
+ self._neutron_client = None
+
+ @property
+ def neutron_client(self):
+ if self._neutron_client:
+ return self._neutron_client
+ from neutronclient.v2_0 import client
+ self._neutron_client = client.Client(
+ username=self.conf.admin_user,
+ password=self.conf.admin_password,
+ tenant_name=self.conf.admin_tenant_name,
+ auth_url=self.conf.auth_url,
+ auth_strategy=self.conf.auth_strategy,
+ region_name=self.conf.auth_region,
+ endpoint_type=self.conf.endpoint_type
+ )
+ return self._neutron_client
+
+ def plug(self, tenant_id, network_id, port_id, datalink_name, mac_address,
+ network=None, bridge=None, namespace=None, prefix=None,
+ protection=False):
+ """Plug in the interface."""
+
+ if net_lib.Datalink.datalink_exists(datalink_name):
+ LOG.info(_("Device %s already exists"), datalink_name)
+ return
+
+ if bridge is None:
+ bridge = self.conf.ovs_integration_bridge
+
+ # check if bridge exists
+ ovs = ovs_lib.OVSBridge(bridge)
+ if not ovs.bridge_exists(bridge):
+ raise exceptions.BridgeDoesNotExist(bridge=bridge)
+
+ if network is None:
+ network = self.neutron_client.show_network(network_id)['network']
+
+ network_type = network.get('provider:network_type')
+ vid = None
+ lower_link = None
+ if network_type == p_const.TYPE_VXLAN:
+ lower_link = 'ovs.vxlan1'
+ elif network_type in [p_const.TYPE_VLAN, p_const.TYPE_FLAT]:
+ phys_network = network.get('provider:physical_network')
+ # For integration bridge the ovs agent will take care of
+ # adding the vlan id
+ if bridge != self.conf.ovs_integration_bridge:
+ vid = network.get('provider:segmentation_id')
+ # need to determine the bridge mapping
+ try:
+ results = get_ovsdb_info('Open_vSwitch', ['other_config'])
+ except Exception as err:
+ LOG.error(_("Failed to retrieve other_config from %s: %s"),
+ bridge, err)
+ raise
+ other_config = results[0]['other_config']
+ if not other_config:
+ msg = (_("'other_config' column in 'Open_vSwitch' OVSDB table "
+ "is not configured. Please configure it so that "
+ "lower-link can be determined for the VNICs"))
+ raise exceptions.Invalid(message=msg)
+ bridge_mappings = other_config.get('bridge_mappings')
+ if not bridge_mappings:
+ msg = (_("'bridge_mappings' info is not set in 'other_config' "
+ "column of 'Open_vSwitch' OVSDB table. Please "
+ "configure it so that lower-link can be determined "
+ "for the VNICs"))
+ raise exceptions.Invalid(message=msg)
+ for bridge_mapping in bridge_mappings.split(','):
+ if phys_network not in bridge_mapping:
+ continue
+ lower_link = bridge_mapping.split(':')[1]
+ break
+ else:
+ # TYPE_GRE and TYPE_LOCAL
+ msg = (_("Unsupported network type: %s") % network_type)
+ LOG.error(msg)
+ raise exceptions.Invalid(message=msg)
+
+ # if lower_link is not set or empty, we need to fail
+ if not lower_link:
+ msg = (_("Failed to determine the lower_link for VNIC "
+ "%s on physical_network %s") %
+ (datalink_name, phys_network))
+ LOG.error(msg)
+ raise exceptions.Invalid(message=msg)
+ dl = net_lib.Datalink(datalink_name)
+ dl.create_vnic(lower_link, mac_address, vid, temp=True)
+
+ attrs = [('external_ids', {'iface-id': port_id,
+ 'iface-status': 'active',
+ 'attached-mac': mac_address})]
+ ovs.replace_port(datalink_name, *attrs)
+
+ def unplug(self, datalink_name, bridge=None, namespace=None, prefix=None):
+ """Unplug the interface."""
+
+ dl = net_lib.Datalink(datalink_name)
+ dl.delete_vnic()
+
+ if bridge is None:
+ bridge = self.conf.ovs_integration_bridge
+
+ # check if bridge exists
+ ovs = ovs_lib.OVSBridge(bridge)
+ if not ovs.bridge_exists(bridge):
+ raise exceptions.BridgeDoesNotExist(bridge=bridge)
+
+ try:
+ ovs.delete_port(datalink_name)
+ LOG.debug("Unplugged interface '%s'", datalink_name)
+ except RuntimeError as err:
+ LOG.error(_("Failed unplugging interface '%s': %s") %
+ (datalink_name, err))
--- a/components/openstack/neutron/files/agent/solaris/net_lib.py Thu May 19 13:53:26 2016 -0700
+++ b/components/openstack/neutron/files/agent/solaris/net_lib.py Thu May 19 14:00:51 2016 -0700
@@ -17,9 +17,15 @@
# @author: Girish Moodalbail, Oracle, Inc.
#
+import eventlet
import netaddr
+from oslo_log import log as logging
+
from neutron.agent.linux import utils
+from neutron.i18n import _LE
+
+LOG = logging.getLogger(__name__)
class CommandBase(object):
@@ -50,10 +56,10 @@
return True
@classmethod
- def ipaddr_exists(cls, ifname, ipaddr):
+ def ipaddr_exists(cls, ifname, ipaddr, ifcheck=True):
- if not cls.ifname_exists(ifname):
- return False
+ if ifcheck and not cls.ifname_exists(ifname):
+ return False
cmd = ['/usr/sbin/ipadm', 'show-addr', '-po', 'addr', ifname]
stdout = cls.execute(cmd)
@@ -83,7 +89,7 @@
if temp:
cmd.append('-t')
self.execute_with_pfexec(cmd)
- elif self.ipaddr_exists(self._ifname, ipaddr):
+ elif self.ipaddr_exists(self._ifname, ipaddr, ifcheck=False):
return
# If an address is IPv6, then to create a static IPv6 address
@@ -96,7 +102,8 @@
mac_addr = stdout.splitlines()[0].strip()
ll_addr = netaddr.EUI(mac_addr).ipv6_link_local()
- if not self.ipaddr_exists(self._ifname, str(ll_addr)):
+ if not self.ipaddr_exists(self._ifname, str(ll_addr),
+ ifcheck=False):
# create a link-local address
cmd = ['/usr/sbin/ipadm', 'create-addr', '-T', 'static', '-a',
str(ll_addr), self._ifname]
@@ -223,3 +230,36 @@
stdout = utils.execute(cmd)
return stdout.splitlines()
+
+
+def _arping(iface_name, address, count):
+ # Set timeout with -w to ensure arping exits in case the interface
+ # is deleted while it is running
+ arping_cmd = ['/usr/sbin/arping', '-A', '-I', iface_name, '-c', count,
+ '-w', 2 * count, address]
+ try:
+ utils.execute(arping_cmd, check_exit_code=False)
+ except Exception:
+ msg = _LE("Failed sending gratuitous ARP to %(addr)s on "
+ "an interface %(iface)s")
+ LOG.exception(msg, {'addr': address, 'iface': iface_name})
+
+
+def send_ip_addr_adv_notif(iface_name, address, config):
+ """Send advance notification of an IP address assignment.
+
+ If the address is in the IPv4 family, send gratuitous ARP.
+
+ If the address is in the IPv6 family, no advance notification is
+ necessary, since the Neighbor Discovery Protocol (NDP), Duplicate
+ Address Discovery (DAD), and (for stateless addresses) router
+ advertisements (RAs) are sufficient for address resolution and
+ duplicate address detection.
+ """
+ count = config.send_arp_for_ha
+
+ def arping():
+ _arping(iface_name, address, count)
+
+ if count > 0 and netaddr.IPAddress(address).version == 4:
+ eventlet.spawn_n(arping)
--- a/components/openstack/neutron/files/dhcp_agent.ini Thu May 19 13:53:26 2016 -0700
+++ b/components/openstack/neutron/files/dhcp_agent.ini Thu May 19 14:00:51 2016 -0700
@@ -14,8 +14,11 @@
# BigSwitch/Floodlight)
# interface_driver = neutron.agent.linux.interface.OVSInterfaceDriver
+# Interface driver for Solaris Open vSwitch
+# interface_driver = neutron.agent.solaris.interface.OVSInterfaceDriver
+
# Name of Open vSwitch bridge to use
-# ovs_integration_bridge = br-int
+# ovs_integration_bridge = br_int0
# Use veth for an OVS interface or not.
# Support kernels with limited namespace support
--- a/components/openstack/neutron/files/l3_agent.ini Thu May 19 13:53:26 2016 -0700
+++ b/components/openstack/neutron/files/l3_agent.ini Thu May 19 14:00:51 2016 -0700
@@ -2,6 +2,22 @@
# Show debugging output in log (sets DEBUG log level output)
# debug = False
+# The Neutron user information for accessing the Neutron API for the
+# OVSInterfaceDriver
+#
+# auth_url = http://localhost:5000/v2.0
+# auth_region = RegionOne
+# admin_tenant_name = %SERVICE_TENANT_NAME%
+# admin_user = %SERVICE_USER%
+# admin_password = %SERVICE_PASSWORD%
+
+# The strategy to be used for auth.
+# Supported values are 'keystone'(default), 'noauth'.
+# auth_strategy = keystone
+
+# Network service endpoint type to pull from the keystone catalog
+# endpoint_type = publicURL
+
# L3 requires that an interface driver be set. Choose the one that best
# matches your plugin.
@@ -9,6 +25,12 @@
# that supports L3 agent
# interface_driver = neutron.agent.linux.interface.OVSInterfaceDriver
+# Interface driver for Solaris Open vSwitch
+# interface_driver = neutron.agent.solaris.interface.OVSInterfaceDriver
+
+# Name of Open vSwitch bridge to use
+# ovs_integration_bridge = br_int0
+
# Use veth for an OVS interface or not.
# Support kernels with limited namespace support
# (e.g. RHEL 6.5) so long as ovs_use_veth is set to True.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openstack/neutron/files/ml2_conf.ini Thu May 19 14:00:51 2016 -0700
@@ -0,0 +1,101 @@
+[ml2]
+# (ListOpt) List of network type driver entrypoints to be loaded from
+# the neutron.ml2.type_drivers namespace.
+#
+# type_drivers = local,flat,vlan,gre,vxlan
+# Example: type_drivers = flat,vlan,gre,vxlan
+type_drivers = flat,vlan,vxlan
+
+# (ListOpt) Ordered list of network_types to allocate as tenant
+# networks. The default value 'local' is useful for single-box testing
+# but provides no connectivity between hosts. In the case of Solaris,
+# 'local' can be achieved by using 'flat' network type and Solaris
+# Etherstubs, so 'local' network type as such is not supported.
+#
+# tenant_network_types = local
+# Example: tenant_network_types = vlan,gre,vxlan
+tenant_network_types = vlan
+
+# (ListOpt) Ordered list of networking mechanism driver entrypoints
+# to be loaded from the neutron.ml2.mechanism_drivers namespace.
+# mechanism_drivers =
+# Example: mechanism_drivers = openvswitch,mlnx
+# Example: mechanism_drivers = arista
+# Example: mechanism_drivers = cisco,logger
+# Example: mechanism_drivers = openvswitch,brocade
+# Example: mechanism_drivers = linuxbridge,brocade
+mechanism_drivers = openvswitch
+
+# (ListOpt) Ordered list of extension driver entrypoints
+# to be loaded from the neutron.ml2.extension_drivers namespace.
+# extension_drivers =
+# Example: extension_drivers = anewextensiondriver
+
+# =========== items for MTU selection and advertisement =============
+# (IntOpt) Path MTU. The maximum permissible size of an unfragmented
+# packet travelling from and to addresses where encapsulated Neutron
+# traffic is sent. Drivers calculate maximum viable MTU for
+# validating tenant requests based on this value (typically,
+# path_mtu - max encap header size). If <=0, the path MTU is
+# indeterminate and no calculation takes place.
+# path_mtu = 0
+
+# (IntOpt) Segment MTU. The maximum permissible size of an
+# unfragmented packet travelling a L2 network segment. If <=0,
+# the segment MTU is indeterminate and no calculation takes place.
+# segment_mtu = 0
+
+# (ListOpt) Physical network MTUs. List of mappings of physical
+# network to MTU value. The format of the mapping is
+# <physnet>:<mtu val>. This mapping allows specifying a
+# physical network MTU value that differs from the default
+# segment_mtu value.
+# physical_network_mtus =
+# Example: physical_network_mtus = physnet1:1550, physnet2:1500
+# ======== end of items for MTU selection and advertisement =========
+
+[ml2_type_flat]
+# (ListOpt) List of physical_network names with which flat networks
+# can be created. Use * to allow flat networks with arbitrary
+# physical_network names.
+#
+# flat_networks =
+# Example:flat_networks = physnet1,physnet2
+# Example:flat_networks = *
+
+[ml2_type_vlan]
+# (ListOpt) List of <physical_network>[:<vlan_min>:<vlan_max>] tuples
+# specifying physical_network names usable for VLAN provider and
+# tenant networks, as well as ranges of VLAN tags on each
+# physical_network available for allocation as tenant networks.
+#
+# network_vlan_ranges =
+# Example: network_vlan_ranges = physnet1:1000:2999,physnet2
+
+[ml2_type_gre]
+# (ListOpt) Comma-separated list of <tun_min>:<tun_max> tuples enumerating ranges of GRE tunnel IDs that are available for tenant network allocation
+# tunnel_id_ranges =
+
+[ml2_type_vxlan]
+# (ListOpt) Comma-separated list of <vni_min>:<vni_max> tuples enumerating
+# ranges of VXLAN VNI IDs that are available for tenant network allocation.
+#
+# vni_ranges =
+
+# (StrOpt) Multicast group for the VXLAN interface. When configured, will
+# enable sending all broadcast traffic to this multicast group. When left
+# unconfigured, will disable multicast VXLAN mode.
+#
+# vxlan_group =
+# Example: vxlan_group = 239.1.1.1
+
+[securitygroup]
+# Controls if neutron security group is enabled or not.
+# It should be false when you use nova security group.
+# enable_security_group = True
+enable_security_group = False
+
+# Use ipset to speed-up the iptables security groups. Enabling ipset support
+# requires that ipset is installed on L2 agent node.
+# enable_ipset = True
+enable_ipset = False
--- a/components/openstack/neutron/files/neutron-dhcp-agent Thu May 19 13:53:26 2016 -0700
+++ b/components/openstack/neutron/files/neutron-dhcp-agent Thu May 19 14:00:51 2016 -0700
@@ -1,6 +1,6 @@
#!/usr/bin/python2.7
-# Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2014, 2016, Oracle and/or its affiliates. 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
@@ -14,10 +14,12 @@
# License for the specific language governing permissions and limitations
# under the License.
+import ConfigParser
import os
import re
import sys
+from openstack_common import is_ml2_plugin
import smf_include
from subprocess import CalledProcessError, Popen, PIPE, check_call
@@ -33,8 +35,8 @@
return False
if output.strip() == value:
return True
- cmd = ["/usr/sbin/ipadm", "set-prop", "-t", "-p", "hostmodel=%s" % value,
- "ipv4"]
+ cmd = ["/usr/bin/pfexec", "/usr/sbin/ipadm", "set-prop", "-t", "-p",
+ "hostmodel=%s" % value, "ipv4"]
p = Popen(cmd, stdout=PIPE, stderr=PIPE)
output, error = p.communicate()
if p.returncode != 0:
@@ -54,11 +56,21 @@
if not set_hostmodel("src-priority"):
return smf_include.SMF_EXIT_ERR_FATAL
- cmd = "/usr/lib/neutron/neutron-dhcp-agent --config-file %s " \
- "--config-file %s" % tuple(sys.argv[2:4])
+ cmd = "/usr/bin/pfexec /usr/lib/neutron/neutron-dhcp-agent " \
+ "--config-file %s --config-file %s" % tuple(sys.argv[2:4])
smf_include.smf_subprocess(cmd)
+def get_ovs_bridge():
+ parser = ConfigParser.ConfigParser()
+ parser.read("/etc/neutron/plugins/openvswitch/ovs_neutron_plugin.ini")
+ try:
+ ovs_bridge = parser.get("ovs", "integration_bridge")
+ except ConfigParser.NoOptionError:
+ ovs_bridge = None
+ return ovs_bridge
+
+
def stop():
try:
# first kill the SMF contract
@@ -78,10 +90,10 @@
# DHCP agent datalinks are always 15 characters in length. They start with
# 'dh', end with '_0', and in between they are hexadecimal digits.
prog = re.compile('dh[0-9A-Fa-f\_]{11}_0')
+ err_delete = False
for ifname in ifnames:
- if not prog.search(ifname):
+ if prog.search(ifname) is None:
continue
-
try:
# first remove the IP
check_call(["/usr/bin/pfexec", "/usr/sbin/ipadm", "delete-ip",
@@ -89,12 +101,18 @@
# next remove the VNIC
check_call(["/usr/bin/pfexec", "/usr/sbin/dladm", "delete-vnic",
ifname])
+ # remove the OVS Port
+ if is_ml2_plugin():
+ check_call(["/usr/bin/pfexec", "/usr/sbin/ovs-vsctl", "--",
+ "--if-exists", "del-port", get_ovs_bridge(),
+ ifname])
except CalledProcessError as err:
- print "failed to remove datalinks used by DHCP agent: %s" % err
- return smf_include.SMF_EXIT_ERR_FATAL
+ print "failed to remove datalink '%s' used by DHCP agent: %s" % \
+ (ifname, err)
+ err_delete = True
# finally reset the hostmodel property
- if not set_hostmodel("weak"):
+ if not set_hostmodel("weak") or err_delete:
return smf_include.SMF_EXIT_ERR_FATAL
return smf_include.SMF_EXIT_OK
--- a/components/openstack/neutron/files/neutron-dhcp-agent.xml Thu May 19 13:53:26 2016 -0700
+++ b/components/openstack/neutron/files/neutron-dhcp-agent.xml Thu May 19 14:00:51 2016 -0700
@@ -1,7 +1,7 @@
<?xml version="1.0" ?>
<!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'>
<!--
- Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 2013, 2016, Oracle and/or its affiliates. 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
@@ -78,6 +78,11 @@
<service_fmri value='svc:/application/openstack/neutron/neutron-server'/>
</dependency>
+ <dependency name='neutron-ovs-agent' grouping='optional_all' restart_on='none'
+ type='service'>
+ <service_fmri value='svc:/application/openstack/neutron/neutron-openvswitch-agent:default' />
+ </dependency>
+
<logfile_attributes permissions='600'/>
<exec_method timeout_seconds="60" type="method" name="start"
@@ -90,8 +95,7 @@
<exec_method timeout_seconds="600" type="method" name="stop"
exec="/lib/svc/method/neutron-dhcp-agent %m %{restarter/contract}">
<method_context>
- <!-- sys_ip_config is required to set 'hostmodel' ipadm property -->
- <method_credential user='neutron' group='neutron' privileges='basic,sys_ip_config' />
+ <method_credential user='neutron' group='neutron'/>
</method_context>
</exec_method>
--- a/components/openstack/neutron/files/neutron-l3-agent Thu May 19 13:53:26 2016 -0700
+++ b/components/openstack/neutron/files/neutron-l3-agent Thu May 19 14:00:51 2016 -0700
@@ -14,15 +14,16 @@
# License for the specific language governing permissions and limitations
# under the License.
+import ConfigParser
import os
import re
from subprocess import CalledProcessError, Popen, PIPE, check_call
import sys
import netaddr
+from openstack_common import is_ml2_plugin
import smf_include
-
from neutron.agent.solaris import packetfilter
from neutron_vpnaas.services.vpn.device_drivers.solaris_ipsec import \
get_vpn_interfaces
@@ -52,7 +53,7 @@
def start():
# verify paths are valid
- for f in sys.argv[2:5]:
+ for f in sys.argv[2:6]:
if not os.path.exists(f) or not os.access(f, os.R_OK):
print '%s does not exist or is not readable' % f
return smf_include.SMF_EXIT_ERR_CONFIG
@@ -89,6 +90,8 @@
cmd = "/usr/bin/pfexec /usr/lib/neutron/neutron-l3-agent " \
"--config-file %s --config-file %s --config-file %s" % \
tuple(sys.argv[2:5])
+ if is_ml2_plugin():
+ cmd += " --config-file %s" % sys.argv[5]
# The VPNaaS shutdown should unplumb all IP tunnels it created. But
# be paranoid and check for lingering tunnels created by OpenStack
@@ -114,6 +117,25 @@
return smf_include.smf_subprocess(cmd)
+def get_ovs_bridge(ifname):
+ # retrieve the right OVS bridge based on the interface name
+ if ifname.startswith('l3i'):
+ config_file = '/etc/neutron/plugins/openvswitch/ovs_neutron_plugin.ini'
+ section = "ovs"
+ option = "integration_bridge"
+ else:
+ config_file = '/etc/neutron/l3_agent.ini'
+ section = "DEFAULT"
+ option = "external_network_bridge"
+ parser = ConfigParser.ConfigParser()
+ parser.read(config_file)
+ try:
+ ovs_bridge = parser.get(section, option)
+ except ConfigParser.NoOptionError:
+ ovs_bridge = None
+ return ovs_bridge
+
+
def stop():
shutdown_vpn()
try:
@@ -140,8 +162,9 @@
# with either 'l3i' or 'l3e', end with '_0', and in between they are
# hexadecimal digits.
prog = re.compile('l3[ie][0-9A-Fa-f\_]{10}_0')
+ err_delete = False
for ifname in ifnames:
- if not prog.search(ifname):
+ if prog.search(ifname) is None:
continue
try:
# first remove the IP
@@ -150,12 +173,19 @@
# next remove the VNIC
check_call(["/usr/bin/pfexec", "/usr/sbin/dladm", "delete-vnic",
ifname])
+ # remove the OVS Port
+ if is_ml2_plugin():
+ ovs_bridge = get_ovs_bridge(ifname)
+ if ovs_bridge:
+ check_call(["/usr/bin/pfexec", "/usr/sbin/ovs-vsctl", "--",
+ "--if-exists", "del-port", ovs_bridge, ifname])
except CalledProcessError as err:
- print "failed to remove datalinks used by L3 agent: %s" % (err)
- return smf_include.SMF_EXIT_ERR_FATAL
+ print "failed to remove datalink '%s' used by L3 agent: %s" % \
+ (ifname, err)
+ err_delete = True
# finally reset the hostmodel property
- if not set_hostmodel("weak"):
+ if not set_hostmodel("weak") or err_delete:
return smf_include.SMF_EXIT_ERR_FATAL
return smf_include.SMF_EXIT_OK
--- a/components/openstack/neutron/files/neutron-l3-agent.xml Thu May 19 13:53:26 2016 -0700
+++ b/components/openstack/neutron/files/neutron-l3-agent.xml Thu May 19 14:00:51 2016 -0700
@@ -55,19 +55,24 @@
type='service'>
<service_fmri value='svc:/application/openstack/neutron/neutron-server'/>
</dependency>
+
+ <dependency name='neutron-ovs-agent' grouping='optional_all' restart_on='none'
+ type='service'>
+ <service_fmri value='svc:/application/openstack/neutron/neutron-openvswitch-agent:default' />
+ </dependency>
<logfile_attributes permissions='600'/>
<exec_method timeout_seconds="60" type="method" name="start"
- exec="/lib/svc/method/neutron-l3-agent %m %{config/config_path} %{config/l3_config_path} %{config/vpn_config_path}">
+ exec="/lib/svc/method/neutron-l3-agent %m %{config/config_path} %{config/l3_config_path} %{config/vpn_config_path} %{config/ml2_config_path}">
<method_context>
- <method_credential user='neutron' group='neutron' />
+ <method_credential user='neutron' group='neutron'/>
</method_context>
</exec_method>
<exec_method timeout_seconds="600" type="method" name="stop"
exec="/lib/svc/method/neutron-l3-agent %m %{restarter/contract}">
<method_context>
- <method_credential user='neutron' group='neutron' />
+ <method_credential user='neutron' group='neutron'/>
</method_context>
</exec_method>
@@ -87,6 +92,8 @@
value='/etc/neutron/l3_agent.ini'/>
<propval name='vpn_config_path' type='astring'
value='/etc/neutron/vpn_agent.ini'/>
+ <propval name='ml2_config_path' type='astring'
+ value='/etc/neutron/plugins/ml2/ml2_conf.ini'/>
</property_group>
</instance>
@@ -128,6 +135,14 @@
</loctext>
</common_name>
</prop_pattern>
+ <prop_pattern name='ml2_config_path' type='astring'
+ required='true'>
+ <common_name>
+ <loctext xml:lang='C'>
+ Filesystem path to Neutron ML2 .ini file
+ </loctext>
+ </common_name>
+ </prop_pattern>
</pg_pattern>
</template>
</service>
--- a/components/openstack/neutron/files/neutron-metadata-agent Thu May 19 13:53:26 2016 -0700
+++ b/components/openstack/neutron/files/neutron-metadata-agent Thu May 19 14:00:51 2016 -0700
@@ -1,6 +1,6 @@
#!/usr/bin/python2.7
-# Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2015, 2016, Oracle and/or its affiliates. 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
@@ -27,8 +27,8 @@
print '%s does not exist or is not readable' % f
return smf_include.SMF_EXIT_ERR_CONFIG
- cmd = "/usr/lib/neutron/neutron-metadata-agent --config-file %s " \
- "--config-file %s" % tuple(sys.argv[2:4])
+ cmd = "/usr/bin/pfexec /usr/lib/neutron/neutron-metadata-agent " \
+ "--config-file %s --config-file %s" % tuple(sys.argv[2:4])
smf_include.smf_subprocess(cmd)
if __name__ == "__main__":
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openstack/neutron/files/neutron-openvswitch-agent Thu May 19 14:00:51 2016 -0700
@@ -0,0 +1,36 @@
+#!/usr/bin/python2.7
+
+# Copyright (c) 2016, Oracle and/or its affiliates. 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.
+
+import os
+import sys
+
+import smf_include
+
+
+def start():
+ # verify paths are valid
+ for f in sys.argv[2:4]:
+ if not os.path.exists(f) or not os.access(f, os.R_OK):
+ print '%s does not exist or is not readable' % f
+ return smf_include.SMF_EXIT_ERR_CONFIG
+
+ cmd = "/usr/bin/pfexec /usr/lib/neutron/neutron-openvswitch-agent " \
+ "--config-file %s --config-file %s" % tuple(sys.argv[2:4])
+ smf_include.smf_subprocess(cmd)
+
+if __name__ == "__main__":
+ os.putenv("LC_ALL", "C")
+ smf_include.smf_main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openstack/neutron/files/neutron-openvswitch-agent.xml Thu May 19 14:00:51 2016 -0700
@@ -0,0 +1,107 @@
+<?xml version="1.0" ?>
+<!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'>
+<!--
+ Copyright (c) 2016, Oracle and/or its affiliates. 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.
+
+ NOTE: This service manifest is not editable; its contents will
+ be overwritten by package or patch operations, including
+ operating system upgrade. Make customizations in a different
+ file.
+-->
+<service_bundle type="manifest" name="neutron-openvswitch-agent">
+
+ <service version="1" type="service"
+ name="application/openstack/neutron/neutron-openvswitch-agent">
+
+ <dependency name='multiuser' grouping='require_all' restart_on='error'
+ type='service'>
+ <service_fmri value='svc:/milestone/multi-user:default' />
+ </dependency>
+
+ <dependency name='upgrade' grouping='require_all' restart_on='none'
+ type='service'>
+ <service_fmri
+ value='svc:/application/openstack/neutron/neutron-upgrade' />
+ </dependency>
+
+ <dependency name='openvswitch' grouping='require_all' restart_on='none'
+ type='service'>
+ <service_fmri
+ value='svc:/application/openvswitch/vswitch-server:default' />
+ </dependency>
+
+ <dependency name='ntp' grouping='optional_all' restart_on='none'
+ type='service'>
+ <service_fmri value='svc:/network/ntp'/>
+ </dependency>
+
+ <dependency name='rabbitmq' grouping='optional_all' restart_on='none'
+ type='service'>
+ <service_fmri value='svc:/application/rabbitmq'/>
+ </dependency>
+
+ <logfile_attributes permissions='600'/>
+
+ <exec_method timeout_seconds="60" type="method" name="start"
+ exec="/lib/svc/method/neutron-openvswitch-agent %m %{config/config_path} %{config/ovs_config_path}">
+ <method_context>
+ <method_credential user='neutron' group='neutron' />
+ </method_context>
+ </exec_method>
+ <exec_method timeout_seconds="60" type="method" name="stop"
+ exec=":kill"/>
+
+ <instance name='default' enabled='false'>
+ <!-- to start/stop/refresh the service -->
+ <property_group name='general' type='framework'>
+ <propval name='action_authorization' type='astring'
+ value='solaris.smf.manage.neutron' />
+ <propval name='value_authorization' type='astring'
+ value='solaris.smf.value.neutron' />
+ </property_group>
+
+ <property_group name='config' type='application'>
+ <propval name='config_path' type='astring'
+ value='/etc/neutron/neutron.conf'/>
+ <propval name='ovs_config_path' type='astring'
+ value='/etc/neutron/plugins/openvswitch/ovs_neutron_plugin.ini'/>
+ </property_group>
+ </instance>
+
+ <template>
+ <common_name>
+ <loctext xml:lang="C">
+ OpenStack Neutron Open vSwitch Agent
+ </loctext>
+ </common_name>
+ <description>
+ <loctext xml:lang="C">
+ neutron-openvswitch-agent is a server daemon that uses an Open vSwitch to
+ create L2 connectivity for instances/datalinks on the node it is running.
+ </loctext>
+ </description>
+ <pg_pattern name='config' type='application' required='true'>
+ <prop_pattern name='config_path' type='astring'
+ required='true'>
+ <common_name>
+ <loctext xml:lang='C'>
+ Filesystem path to Neutron Open vSwitch agent configuration file
+ </loctext>
+ </common_name>
+ </prop_pattern>
+ </pg_pattern>
+ </template>
+ </service>
+</service_bundle>
--- a/components/openstack/neutron/files/neutron-server Thu May 19 13:53:26 2016 -0700
+++ b/components/openstack/neutron/files/neutron-server Thu May 19 14:00:51 2016 -0700
@@ -17,29 +17,35 @@
import os
import sys
+from openstack_common import is_ml2_plugin
import smf_include
from subprocess import CalledProcessError, check_call
def start():
+ cfg_files = sys.argv[2:3]
+ if is_ml2_plugin():
+ cfg_files.append("/etc/neutron/plugins/ml2/ml2_conf.ini")
+ else:
+ cfg_files.append("/etc/neutron/plugins/evs/evs_plugin.ini")
+
+ # verify paths are valid
+ for f in cfg_files:
+ if not os.path.exists(f) or not os.access(f, os.R_OK):
+ print '%s does not exist or is not readable' % f
+ return smf_include.SMF_EXIT_ERR_CONFIG
+
# sync the database to have the Kilo schema
- cmd = ["/usr/bin/neutron-db-manage", "--config-file",
- "/etc/neutron/neutron.conf", "--config-file",
- "/etc/neutron/plugins/evs/evs_plugin.ini", "upgrade", "head"]
+ cmd = ["/usr/bin/neutron-db-manage", "--config-file", cfg_files[0],
+ "--config-file", cfg_files[1], "upgrade", "head"]
try:
check_call(cmd)
except CalledProcessError as err:
print "Unable to create database for Neutron: %s" % err
sys.exit(smf_include.SMF_EXIT_ERR_CONFIG)
- # verify paths are valid
- for f in sys.argv[2:4]:
- if not os.path.exists(f) or not os.access(f, os.R_OK):
- print '%s does not exist or is not readable' % f
- return smf_include.SMF_EXIT_ERR_CONFIG
-
- cmd = "/usr/lib/neutron/neutron-server --config-file %s " \
- "--config-file %s" % tuple(sys.argv[2:4])
+ cmd = "/usr/bin/pfexec /usr/lib/neutron/neutron-server --config-file %s " \
+ "--config-file %s" % tuple(cfg_files)
smf_include.smf_subprocess(cmd)
if __name__ == "__main__":
--- a/components/openstack/neutron/files/neutron-server.xml Thu May 19 13:53:26 2016 -0700
+++ b/components/openstack/neutron/files/neutron-server.xml Thu May 19 14:00:51 2016 -0700
@@ -1,7 +1,7 @@
<?xml version="1.0" ?>
<!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'>
<!--
- Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 2013, 2016, Oracle and/or its affiliates. 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
@@ -54,7 +54,7 @@
<logfile_attributes permissions='600'/>
<exec_method timeout_seconds="600" type="method" name="start"
- exec="/lib/svc/method/neutron-server %m %{config/config_path} %{config/plugin_config_path}">
+ exec="/lib/svc/method/neutron-server %m %{config/config_path}">
<method_context>
<method_credential user='neutron' group='neutron' />
</method_context>
@@ -74,8 +74,6 @@
<property_group name='config' type='application'>
<propval name='config_path' type='astring'
value='/etc/neutron/neutron.conf'/>
- <propval name='plugin_config_path' type='astring'
- value='/etc/neutron/plugins/evs/evs_plugin.ini'/>
</property_group>
</instance>
@@ -100,14 +98,6 @@
</loctext>
</common_name>
</prop_pattern>
- <prop_pattern name='plugin_config_path' type='astring'
- required='true'>
- <common_name>
- <loctext xml:lang='C'>
- Filesystem path to Neutron plugin .ini file
- </loctext>
- </common_name>
- </prop_pattern>
</pg_pattern>
</template>
</service>
--- a/components/openstack/neutron/files/neutron-upgrade Thu May 19 13:53:26 2016 -0700
+++ b/components/openstack/neutron/files/neutron-upgrade Thu May 19 14:00:51 2016 -0700
@@ -47,9 +47,28 @@
('keystone_authtoken', 'admin_user'),
('keystone_authtoken', 'admin_password'),
('keystone_authtoken', 'signing_dir'),
+ # Do not overwrite EVS options with OVS options since upgrade
+ # doesn't handle EVS to OVS upgrade
+ ('DEFAULT', 'core_plugin'),
+ ('DEFAULT', 'service_plugins'),
]
-L3_AGENT_EXCEPTIONS = []
+L3_AGENT_EXCEPTIONS = [
+ # Do not overwrite EVS options with OVS options since upgrade
+ # doesn't handle EVS to OVS migration
+ ('DEFAULT', 'ovs_integration_bridge'),
+ ('DEFAULT', 'interface_driver'),
+ ('DEFAULT', 'external_network_bridge'),
+ ('DEFAULT', 'evs_controller'),
+]
+
+DHCP_AGENT_EXCEPTIONS = [
+ # Do not overwrite EVS options with OVS options since upgrade
+ # doesn't handle EVS to OVS migration
+ ('DEFAULT', 'ovs_integration_bridge'),
+ ('DEFAULT', 'interface_driver'),
+ ('DEFAULT', 'evs_controller'),
+]
METADATA_AGENT_EXCEPTIONS = [
('DEFAULT', 'auth_url'),
--- a/components/openstack/neutron/files/neutron.conf Thu May 19 13:53:26 2016 -0700
+++ b/components/openstack/neutron/files/neutron.conf Thu May 19 14:00:51 2016 -0700
@@ -58,7 +58,9 @@
# entrypoint name.
#
# core_plugin =
-# Example: core_plugin = ml2
+# The ML2 plugin provides support for heterogenous networking technologies
+# in the cloud.
+# core_plugin = ml2
#
# The EVSNeutronPluginV2 Neutron plugin connects to the Solaris Elastic
# Virtual Switch framework to provide virtual networking between Solaris
@@ -73,6 +75,10 @@
#
# service_plugins =
# Example: service_plugins = router,firewall,lbaas,vpnaas,metering
+#
+# This option must be set when the core_plugin is set to ML2 and the
+# supported values are router and vpnaas.
+# service_plugins = router
# Paste configuration file
# api_paste_config = api-paste.ini
--- a/components/openstack/neutron/files/neutron.exec_attr Thu May 19 13:53:26 2016 -0700
+++ b/components/openstack/neutron/files/neutron.exec_attr Thu May 19 14:00:51 2016 -0700
@@ -1,6 +1,9 @@
OpenStack Network Management:solaris:cmd:RO::/usr/bin/neutron-db-manage:\
uid=neutron;gid=neutron
+neutron-agent:solaris:cmd:RO::/usr/sbin/arping:\
+privs=net_rawaccess
+
neutron-agent:solaris:cmd:RO::/usr/sbin/dladm:\
privs=net_icmpaccess,net_privaddr,net_rawaccess,proc_audit,sys_dl_config
--- a/components/openstack/neutron/files/neutron.prof_attr Thu May 19 13:53:26 2016 -0700
+++ b/components/openstack/neutron/files/neutron.prof_attr Thu May 19 14:00:51 2016 -0700
@@ -17,4 +17,4 @@
solaris.smf.manage.routing,\
solaris.smf.value.routing;\
profiles=Elastic Virtual Switch Administration,Network Firewall Management,\
-Network IPsec Management
+Network IPsec Management,OVS Administration
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openstack/neutron/files/ovs_neutron_plugin.ini Thu May 19 14:00:51 2016 -0700
@@ -0,0 +1,162 @@
+[ovs]
+# Do not change this parameter unless you have a good reason to.
+# This is the name of the OVS integration bridge. There is one per hypervisor.
+# The integration bridge acts as a virtual "patch bay". All VM VIFs are
+# attached to this bridge and then "patched" according to their network
+# connectivity.
+#
+# integration_bridge = br-int
+integration_bridge = br_int0
+
+# Only used for the agent if tunnel_id_ranges is not empty for
+# the server. In most cases, the default value should be fine.
+#
+# tunnel_bridge = br-tun
+#
+# In the case of Solaris, tunnel bridge and integration bridge must
+# be the same.
+tunnel_bridge = br_int0
+
+# Peer patch port in integration bridge for tunnel bridge
+# int_peer_patch_port = patch-tun
+
+# Peer patch port in tunnel bridge for integration bridge
+# tun_peer_patch_port = patch-int
+
+# Uncomment this line for the agent if tunnel_id_ranges is not
+# empty for the server. Set local-ip to be the local IP address of
+# this hypervisor.
+#
+# local_ip =
+
+# (ListOpt) Comma-separated list of <physical_network>:<bridge> tuples
+# mapping physical network names to the agent's node-specific OVS
+# bridge names to be used for flat and VLAN networks. The length of
+# bridge names should be no more than 11. Each bridge must
+# exist, and should have a physical network interface configured as a
+# port. All physical networks configured on the server should have
+# mappings to appropriate bridges on each agent.
+#
+# bridge_mappings =
+# Example: bridge_mappings = physnet1:br-eth1
+
+# (BoolOpt) Use veths instead of patch ports to interconnect the integration
+# bridge to physical networks. Support kernel without ovs patch port support
+# so long as it is set to True.
+# use_veth_interconnection = False
+
+# (StrOpt) Which OVSDB backend to use, defaults to 'vsctl'
+# vsctl - The backend based on executing ovs-vsctl
+# native - The backend based on using native OVSDB
+# ovsdb_interface = vsctl
+
+# (StrOpt) The connection string for the native OVSDB backend
+# To enable ovsdb-server to listen on port 6640:
+# ovs-vsctl set-manager ptcp:6640:127.0.0.1
+# ovsdb_connection = tcp:127.0.0.1:6640
+
+[agent]
+# Agent's polling interval in seconds
+# polling_interval = 2
+
+# Minimize polling by monitoring ovsdb for interface changes
+# minimize_polling = True
+
+# When minimize_polling = True, the number of seconds to wait before
+# respawning the ovsdb monitor after losing communication with it
+# ovsdb_monitor_respawn_interval = 30
+
+# (ListOpt) The types of tenant network tunnels supported by the agent.
+# Setting this will enable tunneling support in the agent. This can be set to
+# either 'gre' or 'vxlan'. If this is unset, it will default to [] and
+# disable tunneling support in the agent.
+# You can specify as many values here as your compute hosts supports.
+#
+# tunnel_types =
+# Example: tunnel_types = gre
+# Example: tunnel_types = vxlan
+# Example: tunnel_types = vxlan, gre
+
+# (IntOpt) The port number to utilize if tunnel_types includes 'vxlan'. By
+# default, this will make use of the Open vSwitch default value of '4789' if
+# not specified.
+#
+# vxlan_udp_port =
+# Example: vxlan_udp_port = 8472
+
+# (IntOpt) This is the MTU size of veth interfaces.
+# Do not change unless you have a good reason to.
+# The default MTU size of veth interfaces is 1500.
+# This option has no effect if use_veth_interconnection is False
+# veth_mtu =
+# Example: veth_mtu = 1504
+
+# (BoolOpt) Flag to enable l2-population extension. This option should only be
+# used in conjunction with ml2 plugin and l2population mechanism driver. It'll
+# enable plugin to populate remote ports macs and IPs (using fdb_add/remove
+# RPC calbbacks instead of tunnel_sync/update) on OVS agents in order to
+# optimize tunnel management.
+#
+# l2_population = False
+
+# Enable local ARP responder. Requires OVS 2.1. This is only used by the l2
+# population ML2 MechanismDriver.
+#
+# arp_responder = False
+
+# Enable suppression of ARP responses that don't match an IP address that
+# belongs to the port from which they originate.
+# Note: This prevents the VMs attached to this agent from spoofing,
+# it doesn't protect them from other devices which have the capability to spoof
+# (e.g. bare metal or VMs attached to agents without this flag set to True).
+# Requires a version of OVS that can match ARP headers.
+#
+# prevent_arp_spoofing = False
+
+# (BoolOpt) Set or un-set the don't fragment (DF) bit on outgoing IP packet
+# carrying GRE/VXLAN tunnel. The default value is True.
+#
+# dont_fragment = True
+
+# (BoolOpt) Set to True on L2 agents to enable support
+# for distributed virtual routing.
+#
+# enable_distributed_routing = False
+
+# (IntOpt) Set new timeout in seconds for new rpc calls after agent receives
+# SIGTERM. If value is set to 0, rpc timeout won't be changed"
+#
+# quitting_rpc_timeout = 10
+
+[securitygroup]
+# Firewall driver for realizing neutron security group function.
+# firewall_driver = neutron.agent.firewall.NoopFirewallDriver
+# Example: firewall_driver = neutron.agent.linux.iptables_firewall.OVSHybridIptablesFirewallDriver
+
+# Controls if neutron security group is enabled or not.
+# It should be false when you use nova security group.
+# enable_security_group = True
+enable_security_group = False
+
+#-----------------------------------------------------------------------------
+# Sample Configurations.
+#-----------------------------------------------------------------------------
+#
+# 1. With VLANs on eth1.
+# [ovs]
+# integration_bridge = br-int
+# bridge_mappings = default:br-eth1
+#
+# 2. With GRE tunneling.
+# [ovs]
+# integration_bridge = br-int
+# tunnel_bridge = br-tun
+# local_ip = 10.0.0.3
+#
+# 3. With VXLAN tunneling.
+# [ovs]
+# integration_bridge = br-int
+# tunnel_bridge = br-tun
+# local_ip = 10.0.0.3
+# [agent]
+# tunnel_types = vxlan
--- a/components/openstack/neutron/neutron.p5m Thu May 19 13:53:26 2016 -0700
+++ b/components/openstack/neutron/neutron.p5m Thu May 19 14:00:51 2016 -0700
@@ -89,8 +89,8 @@
group=neutron mode=0644 overlay=allow preserve=renamenew
file path=etc/neutron/plugins/midonet/midonet.ini owner=neutron group=neutron \
mode=0644 overlay=allow preserve=renamenew
-file path=etc/neutron/plugins/ml2/ml2_conf.ini owner=neutron group=neutron \
- mode=0644 overlay=allow preserve=renamenew
+file files/ml2_conf.ini path=etc/neutron/plugins/ml2/ml2_conf.ini \
+ owner=neutron group=neutron mode=0644 overlay=allow preserve=renamenew
file path=etc/neutron/plugins/ml2/ml2_conf_arista.ini owner=neutron \
group=neutron mode=0644 overlay=allow preserve=renamenew
file path=etc/neutron/plugins/ml2/ml2_conf_brocade.ini owner=neutron \
@@ -119,7 +119,8 @@
group=neutron mode=0644 overlay=allow preserve=renamenew
file path=etc/neutron/plugins/opencontrail/contrailplugin.ini owner=neutron \
group=neutron mode=0644 overlay=allow preserve=renamenew
-file path=etc/neutron/plugins/openvswitch/ovs_neutron_plugin.ini owner=neutron \
+file files/ovs_neutron_plugin.ini \
+ path=etc/neutron/plugins/openvswitch/ovs_neutron_plugin.ini owner=neutron \
group=neutron mode=0644 overlay=allow preserve=renamenew
file path=etc/neutron/plugins/ovsvapp/ovsvapp_agent.ini owner=neutron \
group=neutron mode=0644 overlay=allow preserve=renamenew
@@ -143,11 +144,14 @@
file path=lib/svc/manifest/application/openstack/neutron-dhcp-agent.xml
file path=lib/svc/manifest/application/openstack/neutron-l3-agent.xml
file path=lib/svc/manifest/application/openstack/neutron-metadata-agent.xml
+file path=lib/svc/manifest/application/openstack/neutron-openvswitch-agent.xml
file path=lib/svc/manifest/application/openstack/neutron-server.xml
file path=lib/svc/manifest/application/openstack/neutron-upgrade.xml
file files/neutron-dhcp-agent path=lib/svc/method/neutron-dhcp-agent
file files/neutron-l3-agent path=lib/svc/method/neutron-l3-agent
file files/neutron-metadata-agent path=lib/svc/method/neutron-metadata-agent
+file files/neutron-openvswitch-agent \
+ path=lib/svc/method/neutron-openvswitch-agent
file files/neutron-server path=lib/svc/method/neutron-server
file files/neutron-upgrade path=lib/svc/method/neutron-upgrade
file path=usr/bin/neutron-db-manage
@@ -159,6 +163,8 @@
path=usr/lib/neutron/neutron-metadata-agent mode=0555
file usr/bin/neutron-ns-metadata-proxy \
path=usr/lib/neutron/neutron-ns-metadata-proxy mode=0555
+file usr/bin/neutron-openvswitch-agent \
+ path=usr/lib/neutron/neutron-openvswitch-agent mode=0555
file usr/bin/neutron-server path=usr/lib/neutron/neutron-server mode=0555
file usr/bin/neutron-usage-audit path=usr/lib/neutron/neutron-usage-audit \
mode=0555
@@ -965,16 +971,22 @@
# force a dependency on package delivering dnsmasq(8)
depend type=require fmri=__TBD pkg.debug.depend.file=usr/lib/inet/dnsmasq
-# force a dependency on package delivering dladm(1M)
+# force a dependency on package delivering arping(8)
+depend type=require fmri=__TBD pkg.debug.depend.file=usr/sbin/arping
+
+# force a dependency on package delivering dladm(8)
depend type=require fmri=__TBD pkg.debug.depend.file=usr/sbin/dladm
-# force a dependency on package delivering evsadm(1M)
+# force a dependency on package delivering evsadm(8)
depend type=require fmri=__TBD pkg.debug.depend.file=usr/sbin/evsadm
-# force a dependency on package delivering ipadm(1M)
+# force a dependency on package delivering ipadm(8)
depend type=require fmri=__TBD pkg.debug.depend.file=usr/sbin/ipadm
-# force a dependency on package delivering pfctl(1M)
+# force a dependency on package delivering ovs-vsctl(8)
+depend type=require fmri=__TBD pkg.debug.depend.file=usr/sbin/ovs-vsctl
+
+# force a dependency on package delivering pfctl(8)
depend type=require fmri=__TBD pkg.debug.depend.file=usr/sbin/pfctl
# force a dependency on cliff; pkgdepend work is needed to flush this out.
--- a/components/openstack/neutron/patches/01-dhcp-agent-add-solaris.patch Thu May 19 13:53:26 2016 -0700
+++ b/components/openstack/neutron/patches/01-dhcp-agent-add-solaris.patch Thu May 19 14:00:51 2016 -0700
@@ -1,33 +1,6 @@
Changes to the Neutron DHCP agent to port it to Solaris. These changes
will eventually be proposed upstream.
---- neutron-2015.1.2/neutron/agent/dhcp_agent.py.~1~ 2015-10-13 10:35:16.000000000 -0700
-+++ neutron-2015.1.2/neutron/agent/dhcp_agent.py 2016-01-28 23:07:42.219218977 -0800
-@@ -14,9 +14,11 @@
- # License for the specific language governing permissions and limitations
- # under the License.
-
-+import platform
- import sys
-
- from oslo_config import cfg
-+from oslo_utils import importutils
-
- from neutron.agent.common import config
- from neutron.agent.dhcp import config as dhcp_config
-@@ -37,6 +39,12 @@ def register_options():
- cfg.CONF.register_opts(dhcp_config.DNSMASQ_OPTS)
- cfg.CONF.register_opts(metadata_config.DRIVER_OPTS)
- cfg.CONF.register_opts(metadata_config.SHARED_OPTS)
-+ if platform.system() == "SunOS":
-+ interface = \
-+ importutils.import_module("neutron.agent.solaris.interface")
-+ else:
-+ interface = \
-+ importutils.import_module("neutron.agent.linux.interface")
- cfg.CONF.register_opts(interface.OPTS)
-
-
--- neutron-2015.1.2/neutron/agent/linux/external_process.py.~1~ 2015-10-13 10:35:16.000000000 -0700
+++ neutron-2015.1.2/neutron/agent/linux/external_process.py 2016-01-28 23:07:42.221029379 -0800
@@ -15,6 +15,7 @@
@@ -74,20 +47,6 @@
cmdline = '/proc/%s/cmdline' % pid
try:
with open(cmdline, "r") as f:
---- neutron-2015.1.2/neutron/agent/linux/utils.py.~1~ 2015-10-13 10:35:16.000000000 -0700
-+++ neutron-2015.1.2/neutron/agent/linux/utils.py 2016-01-28 23:20:12.736969284 -0800
-@@ -31,7 +31,10 @@ from eventlet import greenthread
- from oslo_config import cfg
- from oslo_log import log as logging
- from oslo_log import loggers
--from oslo_rootwrap import client
-+try:
-+ from oslo_rootwrap import client
-+except:
-+ pass
- from oslo_utils import excutils
-
- from neutron.agent.common import config
--- neutron-2015.1.2/neutron/api/rpc/handlers/dhcp_rpc.py.~1~ 2015-10-13 10:35:16.000000000 -0700
+++ neutron-2015.1.2/neutron/api/rpc/handlers/dhcp_rpc.py 2016-01-28 23:07:42.219930998 -0800
@@ -188,11 +188,13 @@ class DhcpRpcCallback(object):
@@ -109,3 +68,86 @@
except n_exc.NotFound as e:
LOG.warning(e)
+*** neutron-2015.1.2/neutron/agent/linux/utils.py 2015-10-13 10:35:16.000000000 -0700
+--- new/neutron/agent/linux/utils.py 2016-05-14 07:44:40.976050014 -0700
+***************
+*** 18,23 ****
+--- 18,24 ----
+ import grp
+ import httplib
+ import os
++ import platform
+ import pwd
+ import shlex
+ import socket
+***************
+*** 31,37 ****
+ from oslo_config import cfg
+ from oslo_log import log as logging
+ from oslo_log import loggers
+! from oslo_rootwrap import client
+ from oslo_utils import excutils
+
+ from neutron.agent.common import config
+--- 32,41 ----
+ from oslo_config import cfg
+ from oslo_log import log as logging
+ from oslo_log import loggers
+! try:
+! from oslo_rootwrap import client
+! except:
+! pass
+ from oslo_utils import excutils
+
+ from neutron.agent.common import config
+***************
+*** 175,182 ****
+ """Retrieve a list of the pids of child processes of the given pid."""
+
+ try:
+! raw_pids = execute(['ps', '--ppid', pid, '-o', 'pid='],
+! log_fail_as_error=False)
+ except RuntimeError as e:
+ # Unexpected errors are the responsibility of the caller
+ with excutils.save_and_reraise_exception() as ctxt:
+--- 179,190 ----
+ """Retrieve a list of the pids of child processes of the given pid."""
+
+ try:
+! if platform.system() == "SunOS":
+! raw_pids = execute(['/usr/bin/pgrep', '-P', pid],
+! log_fail_as_error=False)
+! else:
+! raw_pids = execute(['ps', '--ppid', pid, '-o', 'pid='],
+! log_fail_as_error=False)
+ except RuntimeError as e:
+ # Unexpected errors are the responsibility of the caller
+ with excutils.save_and_reraise_exception() as ctxt:
+*** neutron-2015.1.2/neutron/agent/dhcp_agent.py 2015-10-13 10:35:16.000000000 -0700
+--- new/neutron/agent/dhcp_agent.py 2016-05-14 07:45:04.012214835 -0700
+***************
+*** 17,27 ****
+--- 17,29 ----
+ import sys
+
+ from oslo_config import cfg
++ from oslo_utils import importutils
+
+ from neutron.agent.common import config
+ from neutron.agent.dhcp import config as dhcp_config
+ from neutron.agent.linux import interface
+ from neutron.agent.metadata import config as metadata_config
++ from neutron.agent.solaris import interface as solaris_interface
+ from neutron.common import config as common_config
+ from neutron.common import topics
+ from neutron.openstack.common import service
+***************
+*** 38,43 ****
+--- 40,46 ----
+ cfg.CONF.register_opts(metadata_config.DRIVER_OPTS)
+ cfg.CONF.register_opts(metadata_config.SHARED_OPTS)
+ cfg.CONF.register_opts(interface.OPTS)
++ cfg.CONF.register_opts(solaris_interface.OPTS)
+
+
+ def main():
--- a/components/openstack/neutron/patches/02-l3-agent-add-solaris.patch Thu May 19 13:53:26 2016 -0700
+++ b/components/openstack/neutron/patches/02-l3-agent-add-solaris.patch Thu May 19 14:00:51 2016 -0700
@@ -1,25 +1,6 @@
Changes to the Neutron L3 agent to port it to Solaris. These changes
will eventually be proposed upstream.
---- neutron-2015.1.2/neutron/agent/l3_agent.py.~1~ 2015-10-13 10:35:16.000000000 -0700
-+++ neutron-2015.1.2/neutron/agent/l3_agent.py 2016-01-28 23:07:42.233773807 -0800
-@@ -14,6 +14,7 @@
- # License for the specific language governing permissions and limitations
- # under the License.
-
-+import platform
- import sys
-
- from oslo_config import cfg
-@@ -46,6 +47,8 @@ def main(manager='neutron.agent.l3.agent
- register_opts(cfg.CONF)
- common_config.init(sys.argv[1:])
- config.setup_logging()
-+ if platform.system() == "SunOS":
-+ manager = 'neutron.agent.evs_l3_agent.EVSL3NATAgent'
- server = neutron_service.Service.create(
- binary='neutron-l3-agent',
- topic=topics.L3_AGENT,
--- neutron-2015.1.2/neutron/agent/linux/daemon.py.~1~ 2015-10-13 10:35:16.000000000 -0700
+++ neutron-2015.1.2/neutron/agent/linux/daemon.py 2016-01-28 23:07:42.234372590 -0800
@@ -18,12 +18,14 @@ import grp
@@ -75,3 +56,46 @@
if _IS_IPV6_ENABLED is None:
disabled_ipv6_path = "/proc/sys/net/ipv6/conf/default/disable_ipv6"
if os.path.exists(disabled_ipv6_path):
+*** neutron-2015.1.2/neutron/agent/l3_agent.py 2015-10-13 10:35:16.000000000 -0700
+--- new/neutron/agent/l3_agent.py 2016-05-14 07:44:53.695396597 -0700
+***************
+*** 14,19 ****
+--- 14,20 ----
+ # License for the specific language governing permissions and limitations
+ # under the License.
+
++ import platform
+ import sys
+
+ from oslo_config import cfg
+***************
+*** 24,29 ****
+--- 25,31 ----
+ from neutron.agent.linux import external_process
+ from neutron.agent.linux import interface
+ from neutron.agent.metadata import config as metadata_config
++ from neutron.agent.solaris import interface as solaris_interface
+ from neutron.common import config as common_config
+ from neutron.common import topics
+ from neutron.openstack.common import service
+***************
+*** 39,44 ****
+--- 41,47 ----
+ config.register_use_namespaces_opts_helper(conf)
+ config.register_agent_state_opts_helper(conf)
+ conf.register_opts(interface.OPTS)
++ conf.register_opts(solaris_interface.OPTS)
+ conf.register_opts(external_process.OPTS)
+
+
+***************
+*** 46,51 ****
+--- 49,56 ----
+ register_opts(cfg.CONF)
+ common_config.init(sys.argv[1:])
+ config.setup_logging()
++ if platform.system() == "SunOS":
++ manager = 'neutron.agent.evs_l3_agent.EVSL3NATAgent'
+ server = neutron_service.Service.create(
+ binary='neutron-l3-agent',
+ topic=topics.L3_AGENT,
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openstack/neutron/patches/06-ml2-ovs-support.patch Thu May 19 14:00:51 2016 -0700
@@ -0,0 +1,590 @@
+Changes to Neutron Open vSwitch agent to port it to Solaris. These changes
+will eventually be proposed upstream.
+
+*** neutron-2015.1.2/neutron/plugins/openvswitch/agent/ovs_neutron_agent.py 2015-10-13 10:35:16.000000000 -0700
+--- new/neutron/plugins/openvswitch/agent/ovs_neutron_agent.py 2016-05-14 07:44:21.485893158 -0700
+***************
+*** 15,20 ****
+--- 15,21 ----
+ # under the License.
+
+ import hashlib
++ import platform
+ import signal
+ import sys
+ import time
+***************
+*** 33,42 ****
+--- 34,45 ----
+ from neutron.agent.linux import ip_lib
+ from neutron.agent import rpc as agent_rpc
+ from neutron.agent import securitygroups_rpc as sg_rpc
++ from neutron.agent.solaris import net_lib
+ from neutron.api.rpc.handlers import dvr_rpc
+ from neutron.common import config as common_config
+ from neutron.common import constants as q_const
+ from neutron.common import exceptions
++ from neutron.common import log
+ from neutron.common import topics
+ from neutron.common import utils as q_utils
+ from neutron import context
+***************
+*** 53,58 ****
+--- 56,70 ----
+ # A placeholder for dead vlans.
+ DEAD_VLAN_TAG = q_const.MAX_VLAN_TAG + 1
+
++ # Solaris specific additional OpenFlow tables to steer packets to/from VNICs
++ # on top of VXLAN datalink.
++ LEARN_FROM_PORTS = 2
++ # Broadcast/Unknown Unicast/Multicast (BUM) tables
++ OUTBOUND_UCAST_BUM_TABLE = 3
++ INBOUND_UCAST_BUM_TABLE = 11
++ INBOUND_UCAST_TABLE = 12
++ INBOUND_BUM_TABLE = 13
++
+
+ class DeviceListRetrievalError(exceptions.NeutronException):
+ message = _("Unable to retrieve port details for devices: %(devices)s "
+***************
+*** 1685,1690 ****
+--- 1697,2190 ----
+ "Agent and Server side."))
+
+
++ class SolarisOVSNeutronAgent(OVSNeutronAgent):
++ """Solaris implementation of OVS L2 Agent"""
++
++ def __init__(self, integ_br, tun_br, local_ip,
++ bridge_mappings, polling_interval, tunnel_types=None,
++ veth_mtu=None, l2_population=False,
++ enable_distributed_routing=False,
++ minimize_polling=False,
++ ovsdb_monitor_respawn_interval=(
++ constants.DEFAULT_OVSDBMON_RESPAWN),
++ arp_responder=False,
++ prevent_arp_spoofing=True,
++ use_veth_interconnection=False,
++ quitting_rpc_timeout=None):
++ '''Please see the Base Class' constructor for parameters info
++ '''
++ self.tun_ofport = None
++ # mapping of VNIC's OpenFlow Port Number (ofport) to
++ # VXLAN segmentation id.
++ self.br_port_segid = {}
++ # mapping of VXLAN sgementation id to set of ports on that segment.
++ # The port is a ovs_lib.VifPort object.
++ self.br_segid_ports = {}
++ # mapping of Neutron port UUID to ovs_lib.VifPort object.
++ self.vif_ports = {}
++ super(SolarisOVSNeutronAgent, self).\
++ __init__(integ_br, tun_br,
++ local_ip, bridge_mappings, polling_interval,
++ tunnel_types, veth_mtu, l2_population,
++ enable_distributed_routing, minimize_polling,
++ ovsdb_monitor_respawn_interval, arp_responder,
++ prevent_arp_spoofing, use_veth_interconnection,
++ quitting_rpc_timeout)
++
++ def _setup_tunnel_port(self, br, port_name, remote_ip, tunnel_type):
++ LOG.debug(_("Setting up tunnel(%s) for remote_ip: %s") %
++ (tunnel_type, remote_ip))
++ if tunnel_type != p_const.TYPE_VXLAN:
++ return
++ self.tun_br_ofports[tunnel_type][remote_ip] = remote_ip
++ remote_ips = self.tun_br_ofports[tunnel_type].values()
++ LOG.debug(_("current list of remote_ips: %s"), remote_ips)
++ for ofport, segmentation_id in self.br_port_segid.iteritems():
++ flood_local_ofports = self.br_segid_ports[segmentation_id]
++ self._mod_flood_to_tun_flows(ofport, remote_ips, segmentation_id,
++ flood_local_ofports - set([ofport]))
++
++ def cleanup_tunnel_port(self, br, remote_ip, tunnel_type):
++ LOG.debug(_("Cleaning up tunnel(%s) for remote_ip: %s") %
++ (tunnel_type, remote_ip))
++ if tunnel_type != p_const.TYPE_VXLAN:
++ return
++ self.tun_br_ofports[tunnel_type].pop(remote_ip, None)
++ remote_ips = self.tun_br_ofports[tunnel_type].values()
++ for ofport, segmentation_id in self.br_port_segid.iteritems():
++ flood_local_ofports = self.br_segid_ports[segmentation_id]
++ self._mod_flood_to_tun_flows(ofport, remote_ips, segmentation_id,
++ flood_local_ofports - set([ofport]))
++
++ # The following methods are called through RPC.
++ # add_fdb_entries(), remove_fdb_entries(), update_fdb_entries()
++ # These methods are overridden from L2populationRpcCallBackMixin class.
++ @log.log
++ def add_fdb_entries(self, context, fdb_entries, host=None):
++ # Needed for L2 Population support. Will be added later
++ pass
++
++ @log.log
++ def remove_fdb_entries(self, context, fdb_entries, host=None):
++ # Needed for L2 Population support. Will be added later
++ pass
++
++ @log.log
++ def update_fdb_entries(self, context, fdb_entries, host=None):
++ # Needed for L2 Population support. Will be added later
++ pass
++
++ def setup_integration_br(self):
++ '''Setup the integration bridge and remove all existing flows.'''
++
++ # Ensure the integration bridge is created.
++ # ovs_lib.OVSBridge.create() will run
++ # ovs-vsctl -- --may-exist add-br BRIDGE_NAME
++ # which does nothing if bridge already exists.
++ self.int_br.create()
++ self.int_br.set_secure_mode()
++
++ self.int_br.remove_all_flows()
++ # Switch all traffic using normal-mode OVS only if tunneling
++ # is disabled. Otherwise, we will need to add various OpenFlow tables
++ # and flows to switch traffic.
++ if not self.enable_tunneling:
++ self.int_br.add_flow(priority=1, actions="normal")
++ # Add a canary flow to int_br to track OVS restarts
++ self.int_br.add_flow(table=constants.CANARY_TABLE, priority=0,
++ actions="drop")
++
++ def setup_physical_bridges(self, bridge_mappings):
++ '''Makes sure that the uplink port for a given physical network
++ exists in the integration bridge.
++ '''
++ self.phys_brs = {}
++ # We do not use either int_ofports or phys_ofports below, however
++ # we need to initialize them to empty values since it is used in
++ # the common code which is mostly no-op for us.
++ self.int_ofports = {}
++ self.phys_ofports = {}
++ ovs = ovs_lib.BaseOVS()
++ for physical_network, uplink_port in bridge_mappings.iteritems():
++ LOG.info(_LI("Mapping physical network %(physical_network)s to "
++ "uplink port %(uplink_port)s"),
++ {'physical_network': physical_network,
++ 'uplink_port': uplink_port})
++ if not ovs.port_exists(uplink_port):
++ LOG.error(_LE("Uplink port %(uplink_port)s for physical "
++ "network %(physical_network)s does not exist. "
++ "Agent terminated!"),
++ {'physical_network': physical_network,
++ 'uplink_port': uplink_port})
++ sys.exit(1)
++ self.phys_brs[physical_network] = uplink_port
++
++ def setup_ancillary_bridges(self, integ_br, tun_br):
++ '''Setup ancillary bridges - for example br-ex.'''
++ ovs = ovs_lib.BaseOVS()
++ ovs_bridges = set(ovs.get_bridges())
++ # Remove all known bridges
++ ovs_bridges.remove(integ_br)
++
++ # Filter list of bridges to those that have external
++ # bridge-id's configured
++ br_names = []
++ for bridge in ovs_bridges:
++ bridge_id = ovs.get_bridge_external_bridge_id(bridge)
++ if bridge_id != bridge:
++ br_names.append(bridge)
++ ovs_bridges.difference_update(br_names)
++ ancillary_bridges = []
++ for bridge in ovs_bridges:
++ br = ovs_lib.OVSBridge(bridge)
++ LOG.info(_LI('Adding %s to list of bridges.'), bridge)
++ ancillary_bridges.append(br)
++ return ancillary_bridges
++
++ def reset_tunnel_br(self, tun_br_name=None):
++ '''(re)initialize the tunnel bridge.
++
++ :param tun_br_name: the name of the tunnel bridge.
++ '''
++ # Solaris doesn't have a separate tunnel bridge, instead we
++ # re-use the integration bridge itself.
++ if self.tun_br is None:
++ self.tun_br = self.int_br
++
++ # create ovs.vxlan1 datalink and add it to integration bridge
++ if not self.local_ip:
++ LOG.error(_LE("local_ip parameter is not set. Cannot have "
++ "tunneling enabled without it. Agent terminated!"))
++ exit(1)
++ if not net_lib.Datalink.datalink_exists("ovs.vxlan1"):
++ # create the required vxlan
++ cmd = ['/usr/sbin/dladm', 'create-vxlan', '-t', '-p',
++ 'addr=%s,vni=flow' % (self.local_ip), 'ovs.vxlan1']
++ try:
++ utils.execute(cmd)
++ except Exception as e:
++ LOG.error(_LE("failed to create VXLAN tunnel end point "
++ "ovs.vxlan1: %s. Agent terminated!") % (e))
++ exit(1)
++ # set openvswitch property to on
++ try:
++ cmd = ['/usr/sbin/dladm', 'show-linkprop', '-p',
++ 'openvswitch', '-co', 'value', 'ovs.vxlan1']
++ stdout = utils.execute(cmd)
++ if stdout.strip() == 'off':
++ cmd = ['/usr/sbin/dladm', 'set-linkprop', '-t', '-p',
++ 'openvswitch=on', 'ovs.vxlan1']
++ utils.execute(cmd)
++ except Exception as e:
++ LOG.error(_LE("failed to set 'openvswitch' property on "
++ "ovs.vxlan1: %s. Agent terminated!") % (e))
++ exit(1)
++
++ attrs = [('type', 'vxlan'),
++ ('options', {'remote_ip': 'flow'}),
++ ('options', {'key': 'flow'})]
++ self.tun_br.replace_port('ovs.vxlan1', *attrs)
++ self.tun_ofport = self.tun_br.get_port_ofport('ovs.vxlan1')
++ if self.tun_ofport == constants.OFPORT_INVALID:
++ LOG.error(_LE("Failed to add ovs.vxlan1 to integration bridge. "
++ "Cannot have tunneling enabled on this agent. "
++ "Agent terminated!"))
++ exit(1)
++
++ def setup_tunnel_br(self):
++ '''Setup the tunnel bridge
++
++ Add all flows to the tunnel bridge.
++ '''
++
++ #
++ # Add flows for inbound packets
++ #
++
++ # Table 0 (default) will sort incoming traffic depending on in_port.
++ # Forward all the packets coming in from all the ports of the bridge
++ # to respective learning tables (LEARN_FROM_TUN or LEARN_FROM_PORTS).
++ self.tun_br.add_flow(priority=1,
++ in_port=self.tun_ofport,
++ actions="resubmit(,%s)" %
++ constants.LEARN_FROM_TUN)
++ self.tun_br.add_flow(priority=0, actions="drop")
++
++ # LEARN_FROM_TUN table will have a single flow using a learn action to
++ # dynamically set-up flows in UCAST_TO_TUN corresponding to remote mac
++ # addresses
++ learned_flow = ("table=%s,"
++ "priority=1,"
++ "hard_timeout=300,"
++ "NXM_NX_TUN_ID[],"
++ "NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],"
++ "load:NXM_NX_TUN_IPV4_SRC[]->NXM_NX_TUN_IPV4_DST[],"
++ "output:NXM_OF_IN_PORT[]" %
++ constants.UCAST_TO_TUN)
++
++ # Once remote mac addresses are learned, packets are sent to
++ # INBOUND_UCAST_BUM_TABLE where the packets are triaged based on
++ # whether they are unicast or broadcast/multicast and sent to
++ # respective tables either for forwarding or flooding
++ self.tun_br.add_flow(table=constants.LEARN_FROM_TUN,
++ priority=1,
++ actions="learn(%s),resubmit(,%s)" %
++ (learned_flow, INBOUND_UCAST_BUM_TABLE))
++
++ # INBOUND_UCAST_TABLE handles forwarding the packet to the right port
++ self.tun_br.add_flow(table=INBOUND_UCAST_BUM_TABLE,
++ priority=0,
++ dl_dst="00:00:00:00:00:00/01:00:00:00:00:00",
++ actions="resubmit(,%s)" % INBOUND_UCAST_TABLE)
++
++ # INBOUND_BUM_TABLE handles flooding for broadcast/unknown-unicast/
++ # multicast packets
++ self.tun_br.add_flow(table=INBOUND_UCAST_BUM_TABLE,
++ priority=0,
++ dl_dst="01:00:00:00:00:00/01:00:00:00:00:00",
++ actions="resubmit(,%s)" % INBOUND_BUM_TABLE)
++
++ # INBOUND_UCAST_TABLE has flows dynamically added by learn action of
++ # a flow in LEARN_FROM_PORTS table. These flows forward a packet to a
++ # port that matches the destination MAC address. If no flow matches,
++ # then the packet will be resubmitted to INBOUND_BUM_TABLE for
++ # flooding.
++ self.tun_br.add_flow(table=INBOUND_UCAST_TABLE,
++ priority=0,
++ actions="resubmit(,%s)" %
++ INBOUND_BUM_TABLE)
++ self.tun_br.add_flow(table=INBOUND_BUM_TABLE,
++ priority=0,
++ actions="drop")
++
++ # Egress unicast will be handled in table UCAST_TO_TUN, where remote
++ # mac addresses will be learned. For now, just add a default flow that
++ # will resubmit unknown unicasts to table FLOOD_TO_TUN to treat them
++ # as broadcasts/multicasts
++ self.tun_br.add_flow(table=constants.UCAST_TO_TUN,
++ priority=0,
++ actions="resubmit(,%s)" %
++ constants.FLOOD_TO_TUN)
++
++ # FLOOD_TO_TUN will handle flooding to tunnels based on segmentation
++ # id. For now, add a default drop action
++ self.tun_br.add_flow(table=constants.FLOOD_TO_TUN,
++ priority=0,
++ actions="drop")
++
++ #
++ # add flows for outbound packets
++ #
++
++ # LEARN_FROM_PORTS table will have a single flow using two learn
++ # actions to dynamically set-up flows in INBOUND_UCAST_TABLE and
++ # UCAST_TO_TUN corresponding to local mac addresses
++ learned_flow = ("table=%s,"
++ "priority=1,"
++ "hard_timeout=300,"
++ "NXM_NX_TUN_ID[],"
++ "NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],"
++ "output:NXM_OF_IN_PORT[]")
++ self.tun_br.add_flow(table=LEARN_FROM_PORTS,
++ priority=1,
++ actions="learn(%s),learn(%s),resubmit(,%s)" %
++ (learned_flow % INBOUND_UCAST_TABLE,
++ learned_flow % constants.UCAST_TO_TUN,
++ OUTBOUND_UCAST_BUM_TABLE))
++
++ # Once local MAC addresses are learned, packets are sent to
++ # OUTBOUND_UCAST_BUM_TABLE where the packet is triaged based on whether
++ # they are unicast or broadcast/multicast and sent to respective tables
++ # either for forwarding or for flooding
++ self.tun_br.add_flow(table=OUTBOUND_UCAST_BUM_TABLE,
++ priority=0,
++ dl_dst="00:00:00:00:00:00/01:00:00:00:00:00",
++ actions="resubmit(,%s)" % constants.UCAST_TO_TUN)
++ # Broadcasts/multicasts go to table FLOOD_TO_TUN that handles flooding
++ self.tun_br.add_flow(table=OUTBOUND_UCAST_BUM_TABLE,
++ priority=0,
++ dl_dst="01:00:00:00:00:00/01:00:00:00:00:00",
++ actions="resubmit(,%s)" % constants.FLOOD_TO_TUN)
++
++ def check_changed_vlans(self, registered_ports):
++ # Not applicable to Solaris
++ return []
++
++ def _mod_flood_to_tun_flows(self, ofport, remote_ips, segmentation_id,
++ local_ofports):
++ LOG.debug(_("Modifying flooding for %s to all %s for VNI %s on %s") %
++ (ofport, remote_ips, segmentation_id, local_ofports))
++ if not local_ofports and not remote_ips:
++ return
++ action_prefix = ""
++ if local_ofports:
++ action_prefix = "output:%s" % _ofport_set_to_str(local_ofports)
++ if not remote_ips:
++ assert local_ofports
++ self.tun_br.mod_flow(table=constants.FLOOD_TO_TUN,
++ in_port="%s" % ofport,
++ actions="%s" % action_prefix)
++ return
++ action_str = ""
++ if action_prefix:
++ action_str = "%s," % action_prefix
++ action_str += "set_tunnel:%s" % segmentation_id
++ # for each of the remote_ip
++ for remote_ip in remote_ips:
++ action_str += ",set_field:%s->tun_dst,output:%s" % \
++ (remote_ip, self.tun_ofport)
++
++ self.tun_br.mod_flow(table=constants.FLOOD_TO_TUN,
++ in_port="%s" % ofport,
++ actions="%s" % action_str)
++
++ def port_bound(self, port, net_uuid,
++ network_type, physical_network,
++ segmentation_id, fixed_ips, device_owner,
++ ovs_restarted):
++ '''Bind port to net_uuid/lsw_id and install flow for inbound traffic
++ to vm.
++
++ :param port: a ovslib.VifPort object.
++ :param net_uuid: the net_uuid this port is to be associated with.
++ :param network_type: the network type ('gre', 'vlan', 'flat', 'local')
++ :param physical_network: the physical network for 'vlan' or 'flat'
++ :param segmentation_id: the VID for 'vlan' or tunnel ID for 'tunnel'
++ :param fixed_ips: the ip addresses assigned to this port
++ :param device_owner: the string indicative of owner of this port
++ :param ovs_restarted: indicates if this is called for an OVS restart.
++ '''
++
++ LOG.info(_LI("Setting up datapath for port: %s connected to "
++ "network: %s of type: %s") % (port.vif_id, net_uuid,
++ network_type))
++
++ if network_type in constants.TUNNEL_NETWORK_TYPES:
++ if self.enable_tunneling:
++ remote_ips = self.tun_br_ofports[network_type].values()
++ # add a flow to flood Broadcast/Unknown Unicast/Multicast
++ # packets from this port to all the remote_ips and local
++ # ports on port's segmentation_id.
++ self._mod_flood_to_tun_flows(port.ofport, remote_ips,
++ segmentation_id, self.br_segid_ports.get(segmentation_id))
++
++ # add segmentation id for all the packets from this port and
++ # send it to table 2 for learning.
++ self.tun_br.add_flow(priority=1,
++ in_port="%s" % (port.ofport),
++ actions="set_tunnel:%s,"
++ "resubmit(,%s)" %
++ (segmentation_id, LEARN_FROM_PORTS))
++
++ # update flow that steers inbound broadcast/unknown/multicast
++ # packets on this segmentation id to all of the ports
++ # (including this port)
++ self.br_port_segid[port.ofport] = segmentation_id
++ if self.br_segid_ports.get(segmentation_id):
++ self.br_segid_ports[segmentation_id].add(port.ofport)
++ else:
++ self.br_segid_ports[segmentation_id] = set([port.ofport])
++ ofports_str = \
++ _ofport_set_to_str(self.br_segid_ports[segmentation_id])
++ self.tun_br.mod_flow(table=INBOUND_BUM_TABLE,
++ tun_id=segmentation_id,
++ actions="output:%s" % ofports_str)
++ # we need to modify flows for other ports that are part of
++ # this segmentation ID
++ ofports = self.br_segid_ports[segmentation_id]
++ for ofport in ofports:
++ if ofport == port.ofport:
++ continue
++ self._mod_flood_to_tun_flows(ofport, remote_ips,
++ segmentation_id,
++ ofports - set([ofport]))
++ self.vif_ports[port.vif_id] = port
++ else:
++ LOG.error(_LE("Cannot provision %(network_type)s network for "
++ "net-id=%(net_uuid)s - tunneling disabled"),
++ {'network_type': network_type,
++ 'net_uuid': net_uuid})
++ elif network_type == p_const.TYPE_FLAT:
++ if physical_network not in self.phys_brs:
++ LOG.error(_LE("Cannot provision flat network for "
++ "net-id=%(net_uuid)s - no uplink port for "
++ "physical_network %(physical_network)s"),
++ {'net_uuid': net_uuid,
++ 'physical_network': physical_network})
++ elif network_type == p_const.TYPE_VLAN:
++ if physical_network in self.phys_brs:
++ # Do not bind a port if it's already bound
++ cur_tag = self.int_br.db_get_val("Port", port.port_name, "tag")
++ if cur_tag != segmentation_id:
++ self.int_br.set_db_attribute("Port", port.port_name, "tag",
++ segmentation_id)
++ if port.ofport != -1:
++ self.int_br.delete_flows(in_port=port.ofport)
++ else:
++ LOG.error(_LE("Cannot provision VLAN network for "
++ "net-id=%(net_uuid)s - no uplink-port for "
++ "physical_network %(physical_network)s"),
++ {'net_uuid': net_uuid,
++ 'physical_network': physical_network})
++ else:
++ LOG.error(_LE("Cannot provision unknown network type "
++ "%(network_type)s for net-id=%(net_uuid)s"),
++ {'network_type': network_type, 'net_uuid': net_uuid})
++
++ def port_unbound(self, vif_id, net_uuid=None):
++ '''Unbind port.
++
++ Removes all the OpenFlow rules associated with the port going away.
++
++ :param vif_id: the id of the vif
++ :param net_uuid: the net_uuid this port is associated with.
++ '''
++ LOG.info(_LI("Removing flows for port: %s" % (vif_id)))
++ if self.enable_tunneling:
++ port = self.vif_ports.pop(vif_id, None)
++ if port is None:
++ return
++ # remove all the OpenFlows that we have added for this port
++ # across all the tables.
++ self.tun_br.delete_flows(in_port=port.ofport)
++ self.tun_br.delete_flows(table=constants.FLOOD_TO_TUN,
++ in_port=port.ofport)
++ segid = self.br_port_segid.pop(port.ofport, None)
++ if segid is None:
++ return
++ self.tun_br.delete_flows(table=INBOUND_UCAST_TABLE,
++ tun_id=segid, dl_dst=port.vif_mac)
++ self.tun_br.delete_flows(table=constants.UCAST_TO_TUN,
++ tun_id=segid, dl_dst=port.vif_mac)
++ if self.br_segid_ports.get(segid) is None:
++ return
++ self.br_segid_ports[segid].discard(port.ofport)
++ ofports = self.br_segid_ports[segid]
++ if ofports:
++ # update brodcast/multicast table to not to include this port
++ ofportstr = _ofport_set_to_str(ofports)
++ self.tun_br.mod_flow(table=INBOUND_BUM_TABLE, tun_id=segid,
++ actions="output:%s" % ofportstr)
++ for ofport in ofports:
++ remote_ips = \
++ self.tun_br_ofports[p_const.TYPE_VXLAN].values()
++ self._mod_flood_to_tun_flows(ofport, remote_ips, segid,
++ ofports - set([ofport]))
++ else:
++ # if this was the last port for that segmentation ID, then
++ # remove all associated flows from broadcast/multicast table
++ self.tun_br.delete_flows(table=INBOUND_BUM_TABLE, tun_id=segid)
++
++ def port_dead(self, port, log_errors=True):
++ # Not required for Solaris
++ pass
++
++ def update_stale_ofport_rules(self):
++ # Not required for Solaris since we don't support ARP spoofing
++ # protection yet
++ pass
++
++
+ def _ofport_set_to_str(ofport_set):
+ return ",".join(map(str, ofport_set))
+
+***************
+*** 1696,1702 ****
+ :returns: a map of agent configuration parameters
+ """
+ try:
+! bridge_mappings = q_utils.parse_mappings(config.OVS.bridge_mappings)
+ except ValueError as e:
+ raise ValueError(_("Parsing bridge_mappings failed: %s.") % e)
+
+--- 2196,2210 ----
+ :returns: a map of agent configuration parameters
+ """
+ try:
+! if platform.system() == "SunOS":
+! # In case of Solaris, we want to allow multiple physical networks
+! # to share the same uplink-port
+! bridge_mappings = \
+! q_utils.parse_mappings(config.OVS.bridge_mappings,
+! unique_values=False)
+! else:
+! bridge_mappings = \
+! q_utils.parse_mappings(config.OVS.bridge_mappings)
+ except ValueError as e:
+ raise ValueError(_("Parsing bridge_mappings failed: %s.") % e)
+
+***************
+*** 1749,1755 ****
+ cfg.CONF.set_default('ip_lib_force_root', True)
+
+ try:
+! agent = OVSNeutronAgent(**agent_config)
+ except RuntimeError as e:
+ LOG.error(_LE("%s Agent terminated!"), e)
+ sys.exit(1)
+--- 2257,2266 ----
+ cfg.CONF.set_default('ip_lib_force_root', True)
+
+ try:
+! if platform.system() == "SunOS":
+! agent = SolarisOVSNeutronAgent(**agent_config)
+! else:
+! agent = OVSNeutronAgent(**agent_config)
+ except RuntimeError as e:
+ LOG.error(_LE("%s Agent terminated!"), e)
+ sys.exit(1)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openstack/neutron/patches/07-ovs-agent-monitor-assertion-fix.patch Thu May 19 14:00:51 2016 -0700
@@ -0,0 +1,53 @@
+In-house patch to fix an issue where-in we are sending two kill events, from
+different contexts, to halt green threads and as a result we are tripping over
+an assert in Neutron OVS agent. It is tracked by
+
+ https://bugs.launchpad.net/neutron/+bug/1350903 (Ovs agent fails to kill ovsdb
+ monitor properly)
+
+and is still open.
+
+
+*** neutron-2015.1.2/neutron/agent/linux/ovsdb_monitor.py Tue Oct 13 10:35:16 2015
+--- neutron-2015.1.2/neutron/agent/linux/ovsdb_monitor.py Wed Mar 23 12:20:07 2016
+***************
+*** 94,101 ****
+ eventlet.sleep()
+
+ def _kill(self, *args, **kwargs):
+ self.data_received = False
+! super(SimpleInterfaceMonitor, self)._kill(*args, **kwargs)
+
+ def _read_stdout(self):
+ data = super(SimpleInterfaceMonitor, self)._read_stdout()
+--- 94,123 ----
+ eventlet.sleep()
+
+ def _kill(self, *args, **kwargs):
++ """Override async_process method.
++
++ Kill the process and the associated watcher greenthreads.
++ :param respawning: Optional, whether respawn will be subsequently
++ attempted.
++ """
+ self.data_received = False
+!
+! if not self._kill_event:
+! return
+!
+! # Halt the greenthreads
+! if not self._kill_event.ready():
+! self._kill_event.send()
+!
+! pid = self.pid
+! if pid:
+! self._kill_process(pid)
+!
+! respawning = kwargs.get('respawning')
+! if not respawning:
+! # Clear the kill event to ensure the process can be
+! # explicitly started again.
+! self._kill_event = None
+
+ def _read_stdout(self):
+ data = super(SimpleInterfaceMonitor, self)._read_stdout()
--- a/components/openstack/nova/files/nova-compute.xml Thu May 19 13:53:26 2016 -0700
+++ b/components/openstack/nova/files/nova-compute.xml Thu May 19 14:00:51 2016 -0700
@@ -41,6 +41,11 @@
<service_fmri value='svc:/network/evs:default' />
</dependency>
+ <dependency name='neutron-ovs-agent' grouping='optional_all' restart_on='none'
+ type='service'>
+ <service_fmri value='svc:/application/openstack/neutron/neutron-openvswitch-agent:default' />
+ </dependency>
+
<dependency name='conductor' grouping='optional_all' restart_on='error'
type='service'>
<service_fmri value='svc:/application/openstack/nova/nova-conductor' />
--- a/components/openstack/nova/files/nova-upgrade Thu May 19 13:53:26 2016 -0700
+++ b/components/openstack/nova/files/nova-upgrade Thu May 19 14:00:51 2016 -0700
@@ -116,6 +116,7 @@
('DEFAULT', 'metadata_workers'),
('DEFAULT', 'lock_path'),
('DEFAULT', 'novncproxy_base_url'),
+ ('DEFAULT', 'ovs_bridge'),
('conductor', 'workers'),
('database', 'connection'),
('keystone_authtoken', 'auth_uri'),
--- a/components/openstack/nova/files/nova.conf Thu May 19 13:53:26 2016 -0700
+++ b/components/openstack/nova/files/nova.conf Thu May 19 14:00:51 2016 -0700
@@ -3277,7 +3277,7 @@
# Name of Integration Bridge used by Open vSwitch (string
# value)
-#ovs_bridge=br-int
+#ovs_bridge=br_int0
# Number of seconds before querying neutron for extensions
# (integer value)
--- a/components/openstack/nova/files/nova.prof_attr Thu May 19 13:53:26 2016 -0700
+++ b/components/openstack/nova/files/nova.prof_attr Thu May 19 14:00:51 2016 -0700
@@ -13,7 +13,8 @@
Do not assign to users. \
Commands required for application/openstack/nova/nova-compute:\
auths=solaris.smf.manage.nova,solaris.smf.modify,solaris.smf.value.nova;\
-profiles=Unified Archive Administration,\
+profiles=OVS Administration,\
+Unified Archive Administration,\
Zone Management,\
Zone Migration,\
Zone Security
--- a/components/openstack/nova/files/solariszones/driver.py Thu May 19 13:53:26 2016 -0700
+++ b/components/openstack/nova/files/solariszones/driver.py Thu May 19 14:00:51 2016 -0700
@@ -27,6 +27,7 @@
import tempfile
import uuid
+from openstack_common import get_ovsdb_info
import rad.bindings.com.oracle.solaris.rad.archivemgr_1 as archivemgr
import rad.bindings.com.oracle.solaris.rad.kstat_1 as kstat
import rad.bindings.com.oracle.solaris.rad.zonemgr_1 as zonemgr
@@ -849,9 +850,9 @@
if entry['device_name'] == rootmp:
root_ci = jsonutils.loads(entry['connection_info'])
# Let's make sure this is a well formed connection_info, by
- # checking if it has a serial key that represents the volume_id.
- # If not check to see if the block device has a volume_id, if so
- # then assign this to the root_ci.serial.
+ # checking if it has a serial key that represents the
+ # volume_id. If not check to see if the block device has a
+ # volume_id, if so then assign this to the root_ci.serial.
#
# If we cannot repair the connection_info then simply do not
# return a root_ci and let the caller decide if they want to
@@ -1001,7 +1002,7 @@
mount = entry['device_name']
self.attach_volume(context, connection_info, instance, mount)
- self._power_on(instance)
+ self._power_on(instance, network_info)
if admin_password is not None:
# Because there is no way to make sure a zone is ready upon
@@ -1332,6 +1333,201 @@
with ZoneConfig(zone) as zc:
zc.setprop('capped-memory', mem_resource, '%dM' % memory_mb)
+ def _plug_vifs(self, instance, network_info):
+ # if the VIF is of EVS type (i.e., vif['type'] is ''),
+ # then nothing to do
+ if network_info is None or not network_info[0]['type']:
+ LOG.debug(_("VIF is an EVS type. Nothing to plug."))
+ return
+
+ # first find out all the anets for a given instance
+ try:
+ out, err = utils.execute('/usr/sbin/dladm', 'show-vnic',
+ '-z', instance['name'],
+ '-po', 'link,macaddress')
+ except Exception as reason:
+ msg = (_("Unable to get ANETs for instance '%s': %s")
+ % (instance['name'], reason))
+ raise exception.NovaException(msg)
+
+ anetdict = {}
+ for anet_maddr in out.strip().splitlines():
+ anet, maddr = anet_maddr.strip().split(':', 1)
+ maddr = maddr.replace('\\', '')
+ maddr = ''.join(['%02x' % int(b, 16) for b in maddr.split(':')])
+ anetdict[maddr] = anet
+
+ LOG.debug(_("List of instance %s's anets: %s") % (instance['name'],
+ anetdict))
+ # we now have a list of VNICs that belong to the VM
+ # we need to map the VNIC to the bridge
+ bridge = CONF.neutron.ovs_bridge
+ for vif in network_info:
+ if vif['type'] != 'ovs':
+ LOG.debug(_("VIF %s is not OVS type") % vif)
+ continue
+ vif_maddr = ''.join(['%02x' % int(b, 16) for b in
+ vif['address'].split(':')])
+ anet = anetdict.get(vif_maddr)
+ if anet is None:
+ LOG.error(_('Failed to add port %s connected to network %s '
+ 'to instance %s') % (vif['ovs_interfaceid'],
+ vif['network']['id'],
+ instance['name']))
+ continue
+ cmd = ['/usr/sbin/ovs-vsctl',
+ '--timeout=%s' % CONF.ovs_vsctl_timeout,
+ '--', '--if-exists', 'del-port', bridge, anet,
+ '--', 'add-port', bridge, anet,
+ '--', 'set', 'Interface', anet,
+ 'external-ids:iface-id=%s' % vif['id'],
+ 'external-ids:iface-status=active',
+ 'external-ids:attached-mac=%s' % vif['address'],
+ 'external-ids:vm-uuid=%s' % instance['uuid']
+ ]
+ try:
+ out, err = utils.execute(*cmd)
+ except Exception as reason:
+ msg = (_("Failed to add VNIC '%s' with MAC address %s to "
+ "OVS Bridge '%s': %s") % (anet, vif['address'],
+ bridge, reason))
+ raise exception.NovaException(msg)
+ LOG.debug(_('Successfully added anet %s with MAC adddress %s') %
+ (anet, vif['address']))
+
+ def _unplug_vifs(self, instance):
+ # Since we don't have VIF info here, we need to find if the anets
+ # were EVS based or OVS based by looking at the CONF setting. In
+ # EVS based cloud neutron.ovs_bridge setting will be set to the
+ # default value of 'br-int'.
+ ovs_bridge = CONF.neutron.ovs_bridge
+ if ovs_bridge == 'br-int':
+ LOG.debug(_("Instance %s doesn't have any OVS based anets") %
+ instance['name'])
+ return
+ # remove the anets from the OVS bridge
+ cmd = ['/usr/sbin/ovs-vsctl', '--timeout=%s' % CONF.ovs_vsctl_timeout,
+ 'list-ports', ovs_bridge]
+ try:
+ out, err = utils.execute(*cmd)
+ except Exception as reason:
+ msg = (_("Unable to get ANETs for instance '%s': %s")
+ % (instance['name'], reason))
+ raise exception.NovaException(msg)
+
+ for port in out.strip().splitlines():
+ if port.split('/')[0] != instance['name']:
+ continue
+ cmd = ['/usr/sbin/ovs-vsctl',
+ '--timeout=%s' % CONF.ovs_vsctl_timeout,
+ '--', '--if-exists', 'del-port', ovs_bridge, port]
+ try:
+ out, err = utils.execute(*cmd)
+ LOG.debug(_('Removed port %s from the OVS bridge %s') %
+ (port, ovs_bridge))
+ except Exception as reason:
+ LOG.warning(_("Unable to remove port %s from the OVS "
+ "bridge %s: %s") % (port, ovs_bridge, reason))
+
+ def _set_evs_info(self, zone, brand, vifid, vif):
+ vport_uuid = vif['id']
+ evs_uuid = vif['network']['id']
+ with ZoneConfig(zone) as zc:
+ if vifid == 0:
+ tenant_id = vif['network']['meta']['tenant_id']
+ zc.setprop('global', 'tenant', tenant_id)
+ zc.setprop('anet', 'configure-allowed-address', 'false')
+ zc.setprop('anet', 'evs', evs_uuid)
+ zc.setprop('anet', 'vport', vport_uuid)
+ else:
+ zc.addresource(
+ 'anet',
+ [zonemgr.Property('configure-allowed-address',
+ 'false'),
+ zonemgr.Property('evs', evs_uuid),
+ zonemgr.Property('vport', vport_uuid)])
+
+ prop_filter = [zonemgr.Property('vport', vport_uuid)]
+ if brand == ZONE_BRAND_SOLARIS:
+ anetname = lookup_resource_property(zc.zone, 'anet',
+ 'linkname', prop_filter)
+ else:
+ anetid = lookup_resource_property(zc.zone, 'anet', 'id',
+ prop_filter)
+ anetname = 'net%s' % anetid
+ return anetname
+
+ def _set_ovs_info(self, context, zone, brand, vifid, vif):
+ # Need to be admin to retrieve provider:network_type attribute
+ network_plugin = neutronv2_api.get_client(context, admin=True)
+ network = network_plugin.show_network(
+ vif['network']['id'])['network']
+ network_type = network['provider:network_type']
+ lower_link = None
+ if network_type == 'vxlan':
+ lower_link = 'ovs.vxlan1'
+ elif network_type in ['vlan', 'flat']:
+ physical_network = network['provider:physical_network']
+ # retrieve the other_config information from Open_vSwitch table
+ try:
+ results = get_ovsdb_info('Open_vSwitch', ['other_config'])
+ except Exception as err:
+ LOG.error(_("Failed to retrieve other_config: %s"), err)
+ raise
+
+ other_config = results[0]['other_config']
+ if not other_config:
+ msg = (_("'other_config' column in 'Open_vSwitch' OVSDB table "
+ "is not configured. Please configure it so that "
+ "lower-link can be determined for the instance's "
+ "anet"))
+ LOG.error(msg)
+ raise exception.NovaException(msg)
+ bridge_mappings = other_config.get('bridge_mappings')
+ if not bridge_mappings:
+ msg = (_("'bridge_mappings' info is not set in 'other_config' "
+ "column of 'Open_vSwitch' OVSDB table. Please "
+ "configure it so that lower-link can be determined "
+ "for the instance's anet"))
+ LOG.error(msg)
+ raise exception.NovaException(msg)
+ for bridge_mapping in bridge_mappings.split(','):
+ if physical_network in bridge_mapping:
+ lower_link = bridge_mapping.split(':')[1]
+ break
+ if not lower_link:
+ msg = (_("Failed to determine the lower_link for vif '%s'") %
+ (vif))
+ LOG.error(msg)
+ raise exception.NovaException(msg)
+ else:
+ # TYPE_GRE and TYPE_LOCAL
+ msg = (_("Unsupported network type: %s") % network_type)
+ LOG.error(msg)
+ raise exception.NovaException(msg)
+
+ with ZoneConfig(zone) as zc:
+ if vifid == 0:
+ zc.setprop('anet', 'lower-link', lower_link)
+ zc.setprop('anet', 'configure-allowed-address', 'false')
+ zc.setprop('anet', 'mac-address', vif['address'])
+ else:
+ zc.addresource('anet',
+ [zonemgr.Property('lower-link', lower_link),
+ zonemgr.Property('configure-allowed-address',
+ 'false'),
+ zonemgr.Property('mac-address', vif['address'])])
+
+ prop_filter = [zonemgr.Property('mac-address', vif['address'])]
+ if brand == ZONE_BRAND_SOLARIS:
+ anetname = lookup_resource_property(zc.zone, 'anet',
+ 'linkname', prop_filter)
+ else:
+ anetid = lookup_resource_property(zc.zone, 'anet', 'id',
+ prop_filter)
+ anetname = 'net%s' % anetid
+ return anetname
+
def _set_network(self, context, name, instance, network_info, brand,
sc_dir):
"""add networking information to the zone."""
@@ -1348,47 +1544,30 @@
zc.removeresources("anet", [zonemgr.Property("id", "0")])
return
- tenant_id = None
- network_plugin = neutronv2_api.get_client(context)
- for netid, network in enumerate(network_info):
- if tenant_id is None:
- tenant_id = network['network']['meta']['tenant_id']
- port_uuid = network['id']
- port = network_plugin.show_port(port_uuid)['port']
- evs_uuid = port['network_id']
- vport_uuid = port['id']
- ip = network['network']['subnets'][0]['ips'][0]['address']
- ip_plen = network['network']['subnets'][0]['cidr'].split('/')[1]
- ip = '/'.join([ip, ip_plen])
- ip_version = network['network']['subnets'][0]['version']
- route = network['network']['subnets'][0]['gateway']['address']
- dns_list = network['network']['subnets'][0]['dns']
+ for vifid, vif in enumerate(network_info):
+ LOG.debug("%s", jsonutils.dumps(vif, indent=5))
+
+ # get all the info common to both EVS or OVS based VIF
+ ip = vif['network']['subnets'][0]['ips'][0]['address']
+ cidr = vif['network']['subnets'][0]['cidr']
+ ip_cidr = "%s/%s" % (ip, cidr.split('/')[1])
+ ip_version = vif['network']['subnets'][0]['version']
+ dhcp_server = \
+ vif['network']['subnets'][0]['meta'].get('dhcp_server')
+ enable_dhcp = dhcp_server is not None
+ route = vif['network']['subnets'][0]['gateway']['address']
+ dns_list = vif['network']['subnets'][0]['dns']
nameservers = []
for dns in dns_list:
if dns['type'] == 'dns':
nameservers.append(dns['address'])
- with ZoneConfig(zone) as zc:
- if netid == 0:
- zc.setprop('anet', 'configure-allowed-address', 'false')
- zc.setprop('anet', 'evs', evs_uuid)
- zc.setprop('anet', 'vport', vport_uuid)
- else:
- zc.addresource(
- 'anet',
- [zonemgr.Property('configure-allowed-address',
- 'false'),
- zonemgr.Property('evs', evs_uuid),
- zonemgr.Property('vport', vport_uuid)])
-
- filter = [zonemgr.Property('vport', vport_uuid)]
- if brand == ZONE_BRAND_SOLARIS:
- linkname = lookup_resource_property(zc.zone, 'anet',
- 'linkname', filter)
- else:
- id = lookup_resource_property(zc.zone, 'anet', 'id',
- filter)
- linkname = 'net%s' % id
+ # for EVS based VIFs the type is empty since EVS plugin
+ # doesn't support portbinding extension
+ if not vif['type']:
+ anetname = self._set_evs_info(zone, brand, vifid, vif)
+ else:
+ anetname = self._set_ovs_info(context, zone, brand, vifid, vif)
# create the required sysconfig file (or skip if this is part of a
# resize or evacuate process)
@@ -1399,27 +1578,17 @@
task_states.REBUILD_SPAWNING] or \
(tstate == task_states.REBUILD_SPAWNING and
instance.system_metadata['rebuilding']):
- subnet_uuid = port['fixed_ips'][0]['subnet_id']
- subnet = network_plugin.show_subnet(subnet_uuid)['subnet']
-
- if subnet['enable_dhcp']:
- tree = sysconfig.create_ncp_defaultfixed('dhcp', linkname,
- netid, ip_version)
+ if enable_dhcp:
+ tree = sysconfig.create_ncp_defaultfixed('dhcp', anetname,
+ vifid, ip_version)
else:
tree = sysconfig.create_ncp_defaultfixed('static',
- linkname, netid,
- ip_version, ip,
- route,
- nameservers)
-
- fp = os.path.join(sc_dir, 'evs-network-%d.xml' % netid)
+ anetname, vifid, ip_version, ip_cidr, route,
+ nameservers)
+
+ fp = os.path.join(sc_dir, 'zone-network-%d.xml' % vifid)
sysconfig.create_sc_profile(fp, tree)
- if tenant_id is not None:
- # set the tenant id
- with ZoneConfig(zone) as zc:
- zc.setprop('global', 'tenant', tenant_id)
-
def _set_suspend(self, instance):
"""Use the instance name to specify the pathname for the suspend image.
"""
@@ -1764,7 +1933,7 @@
LOG.debug(_("Installation of instance '%s' (%s) complete") %
(name, instance['display_name']))
- def _power_on(self, instance):
+ def _power_on(self, instance, network_info):
"""Power on a Solaris Zone."""
name = instance['name']
zone = self._get_zone_by_name(name)
@@ -1794,6 +1963,7 @@
try:
zone.boot(bootargs)
+ self._plug_vifs(instance, network_info)
except Exception as ex:
reason = zonemgr_strerror(ex)
LOG.error(_("Unable to power on instance '%s' via zonemgr(3RAD): "
@@ -1909,7 +2079,7 @@
self._create_config(context, instance, network_info,
connection_info, sc_dir, admin_password)
self._install(instance, image, sc_dir)
- self._power_on(instance)
+ self._power_on(instance, network_info)
except Exception as ex:
reason = zonemgr_strerror(ex)
LOG.error(_("Unable to spawn instance '%s' via zonemgr(3RAD): %s")
@@ -1962,6 +2132,7 @@
raise exception.InstanceNotFound(instance_id=name)
try:
+ self._unplug_vifs(instance)
if halt_type == 'SOFT':
zone.shutdown()
else:
@@ -2136,7 +2307,7 @@
raise exception.InstanceNotFound(instance_id=name)
if self._get_state(zone) == power_state.SHUTDOWN:
- self._power_on(instance)
+ self._power_on(instance, network_info)
return
bootargs = []
@@ -2161,11 +2332,13 @@
zc.clear_resource_props('global', ['bootargs'])
try:
+ self._unplug_vifs(instance)
if reboot_type == 'SOFT':
bootargs.insert(0, '-r')
zone.shutdown(bootargs)
else:
zone.reboot(bootargs)
+ self._plug_vifs(instance, network_info)
except Exception as ex:
reason = zonemgr_strerror(ex)
LOG.error(_("Unable to reboot instance '%s' via zonemgr(3RAD): %s")
@@ -2830,7 +3003,7 @@
entry['mount_device'])
if power_on:
- self._power_on(instance)
+ self._power_on(instance, network_info)
if brand == ZONE_BRAND_SOLARIS:
return
@@ -2989,7 +3162,7 @@
del instance.system_metadata['new_instance_volid']
self._volume_api.delete(context, new_rvid)
- self._power_on(instance)
+ self._power_on(instance, network_info)
def pause(self, instance):
"""Pause the specified instance.
@@ -3042,6 +3215,7 @@
self._set_suspend(instance)
zone.suspend()
+ self._unplug_vifs(instance)
except Exception as ex:
reason = zonemgr_strerror(ex)
LOG.error(_("Unable to suspend instance '%s' via "
@@ -3075,6 +3249,7 @@
try:
zone.boot()
+ self._plug_vifs(instance, network_info)
except Exception as ex:
reason = zonemgr_strerror(ex)
LOG.error(_("Unable to resume instance '%s' via zonemgr(3RAD): %s")
@@ -3098,7 +3273,7 @@
power_state.SHUTDOWN):
return
- self._power_on(instance)
+ self._power_on(instance, network_info)
def rescue(self, context, instance, network_info, image_meta,
rescue_password):
@@ -3139,7 +3314,7 @@
:param instance: nova.objects.instance.Instance
"""
- self._power_on(instance)
+ self._power_on(instance, network_info)
def soft_delete(self, instance):
"""Soft delete the specified instance.
@@ -3387,8 +3562,7 @@
:param instance: instance object reference
:param network_info: instance network information
"""
- raise NotImplementedError(_("Hypervisor driver does not support "
- "post_live_migration_at_source method"))
+ self._unplug_vifs(instance)
def post_live_migration_at_destination(self, context, instance,
network_info,
@@ -3401,7 +3575,7 @@
:param network_info: instance network information
:param block_migration: if true, post operation of block_migration.
"""
- pass
+ self._plug_vifs(instance, network_info)
def check_instance_shared_storage_local(self, context, instance):
"""Check if instance files located on shared storage.