PSARC/2016/251 OpenStack Neutron ML2 framework and OVS mechanism driver
authorGirish Moodalbail <Girish.Moodalbail@oracle.COM>
Thu, 19 May 2016 14:00:51 -0700
changeset 6031 1aaf20a19738
parent 6030 494adc5697ee
child 6032 e4905501cafd
PSARC/2016/251 OpenStack Neutron ML2 framework and OVS mechanism driver 22600422 Add support for OVS in OpenStack Neutron
components/openstack/common/files/openstack_common.py
components/openstack/neutron/Makefile
components/openstack/neutron/files/agent/evs_l3_agent.py
components/openstack/neutron/files/agent/solaris/dhcp.py
components/openstack/neutron/files/agent/solaris/interface.py
components/openstack/neutron/files/agent/solaris/net_lib.py
components/openstack/neutron/files/dhcp_agent.ini
components/openstack/neutron/files/l3_agent.ini
components/openstack/neutron/files/ml2_conf.ini
components/openstack/neutron/files/neutron-dhcp-agent
components/openstack/neutron/files/neutron-dhcp-agent.xml
components/openstack/neutron/files/neutron-l3-agent
components/openstack/neutron/files/neutron-l3-agent.xml
components/openstack/neutron/files/neutron-metadata-agent
components/openstack/neutron/files/neutron-openvswitch-agent
components/openstack/neutron/files/neutron-openvswitch-agent.xml
components/openstack/neutron/files/neutron-server
components/openstack/neutron/files/neutron-server.xml
components/openstack/neutron/files/neutron-upgrade
components/openstack/neutron/files/neutron.conf
components/openstack/neutron/files/neutron.exec_attr
components/openstack/neutron/files/neutron.prof_attr
components/openstack/neutron/files/ovs_neutron_plugin.ini
components/openstack/neutron/neutron.p5m
components/openstack/neutron/patches/01-dhcp-agent-add-solaris.patch
components/openstack/neutron/patches/02-l3-agent-add-solaris.patch
components/openstack/neutron/patches/06-ml2-ovs-support.patch
components/openstack/neutron/patches/07-ovs-agent-monitor-assertion-fix.patch
components/openstack/nova/files/nova-compute.xml
components/openstack/nova/files/nova-upgrade
components/openstack/nova/files/nova.conf
components/openstack/nova/files/nova.prof_attr
components/openstack/nova/files/solariszones/driver.py
--- 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.