Merge "Remove the embrane plugin"

This commit is contained in:
Jenkins 2015-10-16 22:06:28 +00:00 committed by Gerrit Code Review
commit b88cd283a1
32 changed files with 17 additions and 1345 deletions

View File

@ -1,41 +0,0 @@
[heleos]
#configure the ESM management address
#in the first version of this plugin, only one ESM can be specified
#Example:
#esm_mgmt=
#configure admin username and password
#admin_username=
#admin_password=
#router image id
#Example:
#router_image=932ce713-e210-3d54-a0a5-518b0b5ee1b0
#mgmt shared security zone id
#defines the shared management security zone. Each tenant can have a private one configured through the ESM
#Example:
#mgmt_id=c0bc9b6c-f110-46cf-bb01-733bfe4b5a1a
#in-band shared security zone id
#defines the shared in-band security zone. Each tenant can have a private one configured through the ESM
#Example:
#inband_id=a6b7999d-3806-4b04-81f6-e0c5c8271afc
#oob-band shared security zone id
#defines the shared out-of-band security zone. Each tenant can have a private one configured through the ESM
#Example:
#oob_id=e7eda5cc-b977-46cb-9c14-cab43c1b7871
#dummy security zone id
#defines the dummy security zone ID. this security zone will be used by the DVAs with no neutron interfaces
#Example:
#dummy_utif_id=d9911310-25fc-4733-a2e0-c0eda024ef08
#resource pool id
#define the shared resource pool. Each tenant can have a private one configured through the ESM
#Example
#resource_pool_id=
#define if the requests have to be executed asynchronously by the plugin or not
#async_requests=

View File

@ -20,7 +20,7 @@ VPNAAS_TABLES = ['vpnservices', 'ipsecpolicies', 'ipsecpeercidrs',
LBAAS_TABLES = ['vips', 'sessionpersistences', 'pools', 'healthmonitors',
'poolstatisticss', 'members', 'poolloadbalanceragentbindings',
'embrane_pool_port', 'poolmonitorassociations']
'poolmonitorassociations']
FWAAS_TABLES = ['firewall_rules', 'firewalls', 'firewall_policies']

View File

@ -1,6 +1,3 @@
# Copyright 2013 Embrane, Inc.
# 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
@ -12,10 +9,22 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
from neutron.plugins.embrane.common import exceptions as embrane_exc
"""Drop embrane plugin table
Revision ID: 1b294093239c
Revises: 4af11ca47297
Create Date: 2015-10-09 14:07:59.968597
"""
# revision identifiers, used by Alembic.
revision = '1b294093239c'
down_revision = '4af11ca47297'
from alembic import op
class UtifInfoError(embrane_exc.EmbranePluginException):
message = _("Cannot retrieve utif info for the following reason: "
"%(err_msg)s")
def upgrade():
op.drop_table('embrane_pool_port')

View File

@ -1,9 +0,0 @@
Embrane Neutron Plugin
This plugin interfaces OpenStack Neutron with Embrane's heleos platform, which
provides layer 3-7 network services for cloud environments.
L2 connectivity is leveraged by one of the supported existing plugins.
For more details on use, configuration and implementation please refer to:
http://wiki.openstack.org/wiki/Neutron/EmbraneNeutronPlugin

View File

@ -1,131 +0,0 @@
# Copyright 2013 Embrane, Inc.
# 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.
from eventlet import greenthread
from eventlet import queue
from heleosapi import constants as h_con
from heleosapi import exceptions as h_exc
from oslo_log import log as logging
from neutron.i18n import _LE
from neutron.plugins.embrane.agent.operations import router_operations
from neutron.plugins.embrane.common import constants as p_con
from neutron.plugins.embrane.common import contexts as ctx
LOG = logging.getLogger(__name__)
class Dispatcher(object):
def __init__(self, plugin, async=True):
self._async = async
self._plugin = plugin
self.sync_items = dict()
def dispatch_l3(self, d_context, args=(), kwargs={}):
item = d_context.item
event = d_context.event
n_context = d_context.n_context
chain = d_context.chain
item_id = item["id"]
handlers = router_operations.handlers
if event in handlers:
for f in handlers[event]:
first_run = False
if item_id not in self.sync_items:
self.sync_items[item_id] = (queue.Queue(),)
first_run = True
self.sync_items[item_id][0].put(
ctx.OperationContext(event, n_context, item, chain, f,
args, kwargs))
t = None
if first_run:
t = greenthread.spawn(self._consume_l3,
item_id,
self.sync_items[item_id][0],
self._plugin,
self._async)
self.sync_items[item_id] += (t,)
if not self._async:
t = self.sync_items[item_id][1]
t.wait()
def _consume_l3(self, sync_item, sync_queue, plugin, a_sync):
current_state = None
while True:
try:
# If the DVA is deleted, the thread (and the associated queue)
# can die as well
if current_state == p_con.Status.DELETED:
del self.sync_items[sync_item]
return
try:
# If synchronous op, empty the queue as fast as possible
operation_context = sync_queue.get(
block=a_sync,
timeout=p_con.QUEUE_TIMEOUT)
except queue.Empty:
del self.sync_items[sync_item]
return
# Execute the preliminary operations
(operation_context.chain and
operation_context.chain.execute_all())
# Execute the main operation, a transient state is maintained
# so that the consumer can decide if it has
# to be burned to the DB
transient_state = None
try:
dva_state = operation_context.function(
plugin._esm_api,
operation_context.n_context.tenant_id,
operation_context.item,
*operation_context.args,
**operation_context.kwargs)
if dva_state == p_con.Status.DELETED:
transient_state = dva_state
else:
if not dva_state:
transient_state = p_con.Status.ERROR
elif dva_state == h_con.DvaState.POWER_ON:
transient_state = p_con.Status.ACTIVE
else:
transient_state = p_con.Status.READY
except (h_exc.PendingDva, h_exc.DvaNotFound,
h_exc.BrokenInterface, h_exc.DvaCreationFailed,
h_exc.DvaCreationPending, h_exc.BrokenDva,
h_exc.ConfigurationFailed) as ex:
LOG.warning(p_con.error_map[type(ex)], ex)
transient_state = p_con.Status.ERROR
except h_exc.DvaDeleteFailed as ex:
LOG.warning(p_con.error_map[type(ex)], ex)
transient_state = p_con.Status.DELETED
finally:
# if the returned transient state is None, no operations
# are required on the DVA status
if transient_state:
if transient_state == p_con.Status.DELETED:
current_state = plugin._delete_router(
operation_context.n_context,
operation_context.item["id"])
# Error state cannot be reverted
elif transient_state != p_con.Status.ERROR:
current_state = plugin._update_neutron_state(
operation_context.n_context,
operation_context.item,
transient_state)
except Exception:
LOG.exception(_LE("Unhandled exception occurred"))

View File

@ -1,153 +0,0 @@
# Copyright 2013 Embrane, Inc.
# 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 functools
from heleosapi import exceptions as h_exc
from oslo_log import log as logging
from neutron.i18n import _LW
from neutron.plugins.embrane.common import constants as p_con
LOG = logging.getLogger(__name__)
handlers = dict()
def handler(event, handler):
def wrap(f):
if event not in handler.keys():
new_func_list = [f]
handler[event] = new_func_list
else:
handler[event].append(f)
@functools.wraps(f)
def wrapped_f(*args, **kwargs):
return f(*args, **kwargs)
return wrapped_f
return wrap
@handler(p_con.Events.CREATE_ROUTER, handlers)
def _create_dva_and_assign_address(api, tenant_id, neutron_router,
flavor, utif_info=None,
ip_allocation_info=None):
"""Creates a new router, and assign the gateway interface if any."""
dva = api.create_router(tenant_id=tenant_id,
router_id=neutron_router["id"],
name=neutron_router["name"],
flavor=flavor,
up=neutron_router["admin_state_up"])
try:
if utif_info:
api.grow_interface(utif_info, neutron_router["admin_state_up"],
tenant_id, neutron_router["id"])
if ip_allocation_info:
dva = api.allocate_address(neutron_router["id"],
neutron_router["admin_state_up"],
ip_allocation_info)
except h_exc.PreliminaryOperationsFailed as ex:
raise h_exc.BrokenInterface(err_msg=str(ex))
state = api.extract_dva_state(dva)
return state
@handler(p_con.Events.UPDATE_ROUTER, handlers)
def _update_dva_and_assign_address(api, tenant_id, neutron_router,
utif_info=None, ip_allocation_info=None,
routes_info=[]):
name = neutron_router["name"]
up = neutron_router["admin_state_up"]
r_id = neutron_router["id"]
if ip_allocation_info or routes_info:
up = True
dva = api.update_dva(tenant_id=tenant_id, router_id=r_id, name=name,
up=up, utif_info=utif_info)
if ip_allocation_info:
api.allocate_address(r_id, up, ip_allocation_info)
if routes_info:
api.delete_extra_routes(r_id, up)
api.set_extra_routes(r_id, neutron_router["admin_state_up"],
routes_info)
return api.extract_dva_state(dva)
@handler(p_con.Events.DELETE_ROUTER, handlers)
def _delete_dva(api, tenant_id, neutron_router):
try:
api.delete_dva(tenant_id, neutron_router["id"])
except h_exc.DvaNotFound:
LOG.warning(_LW("The router %s had no physical representation, "
"likely already deleted"), neutron_router["id"])
return p_con.Status.DELETED
@handler(p_con.Events.GROW_ROUTER_IF, handlers)
def _grow_dva_iface_and_assign_address(api, tenant_id, neutron_router,
utif_info=None,
ip_allocation_info=None):
try:
dva = api.grow_interface(utif_info, neutron_router["admin_state_up"],
tenant_id, neutron_router["id"])
if ip_allocation_info:
dva = api.allocate_address(neutron_router["id"],
neutron_router["admin_state_up"],
ip_allocation_info)
except h_exc.PreliminaryOperationsFailed as ex:
raise h_exc.BrokenInterface(err_msg=str(ex))
state = api.extract_dva_state(dva)
return state
@handler(p_con.Events.SHRINK_ROUTER_IF, handlers)
def _shrink_dva_iface(api, tenant_id, neutron_router, port_id):
try:
dva = api.shrink_interface(tenant_id, neutron_router["id"],
neutron_router["admin_state_up"], port_id)
except h_exc.InterfaceNotFound:
LOG.warning(_LW("Interface %s not found in the heleos back-end, "
"likely already deleted"), port_id)
return (p_con.Status.ACTIVE if neutron_router["admin_state_up"] else
p_con.Status.READY)
except h_exc.PreliminaryOperationsFailed as ex:
raise h_exc.BrokenInterface(err_msg=str(ex))
state = api.extract_dva_state(dva)
return state
@handler(p_con.Events.SET_NAT_RULE, handlers)
def _create_nat_rule(api, tenant_id, neutron_router, nat_info=None):
dva = api.create_nat_entry(neutron_router["id"],
neutron_router["admin_state_up"], nat_info)
state = api.extract_dva_state(dva)
return state
@handler(p_con.Events.RESET_NAT_RULE, handlers)
def _delete_nat_rule(api, tenant_id, neutron_router, floating_ip_id):
dva = api.remove_nat_entry(neutron_router["id"],
neutron_router["admin_state_up"],
floating_ip_id)
state = api.extract_dva_state(dva)
return state

View File

@ -1,374 +0,0 @@
# Copyright 2013 Embrane, Inc.
# 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.
from heleosapi import backend_operations as h_op
from heleosapi import constants as h_con
from heleosapi import exceptions as h_exc
from oslo_config import cfg
from oslo_log import log as logging
from sqlalchemy.orm import exc
from neutron.common import constants as l3_constants
from neutron.common import exceptions as neutron_exc
from neutron.db import extraroute_db
from neutron.db import l3_db
from neutron.db import models_v2
from neutron.extensions import l3
from neutron.i18n import _LE
from neutron.plugins.embrane.agent import dispatcher
from neutron.plugins.embrane.common import config # noqa
from neutron.plugins.embrane.common import constants as p_con
from neutron.plugins.embrane.common import contexts as embrane_ctx
from neutron.plugins.embrane.common import operation
from neutron.plugins.embrane.common import utils
LOG = logging.getLogger(__name__)
conf = cfg.CONF.heleos
class EmbranePlugin(object):
"""Embrane Neutron plugin.
uses the heleos(c) platform and a support L2 plugin to leverage networking
in cloud environments.
"""
_l3super = extraroute_db.ExtraRoute_db_mixin
def __init__(self):
pass
def _run_embrane_config(self):
# read configurations
config_esm_mgmt = conf.esm_mgmt
config_admin_username = conf.admin_username
config_admin_password = conf.admin_password
config_router_image_id = conf.router_image
config_security_zones = {h_con.SzType.IB: conf.inband_id,
h_con.SzType.OOB: conf.oob_id,
h_con.SzType.MGMT: conf.mgmt_id,
h_con.SzType.DUMMY: conf.dummy_utif_id}
config_resource_pool = conf.resource_pool_id
self._embrane_async = conf.async_requests
self._esm_api = h_op.BackendOperations(
esm_mgmt=config_esm_mgmt,
admin_username=config_admin_username,
admin_password=config_admin_password,
router_image_id=config_router_image_id,
security_zones=config_security_zones,
resource_pool=config_resource_pool)
self._dispatcher = dispatcher.Dispatcher(self, self._embrane_async)
def _make_router_dict(self, *args, **kwargs):
return self._l3super._make_router_dict(self, *args, **kwargs)
def _delete_router(self, context, router_id):
self._l3super.delete_router(self, context, router_id)
def _update_db_router_state(self, context, neutron_router, dva_state):
if not dva_state:
new_state = p_con.Status.ERROR
elif dva_state == h_con.DvaState.POWER_ON:
new_state = p_con.Status.ACTIVE
else:
new_state = p_con.Status.READY
self._set_db_router_state(context, neutron_router, new_state)
return new_state
def _set_db_router_state(self, context, neutron_router, new_state):
return utils.set_db_item_state(context, neutron_router, new_state)
def _update_db_interfaces_state(self, context, neutron_router):
router_ports = self.get_ports(context,
{"device_id": [neutron_router["id"]]})
self._esm_api.update_ports_status(neutron_router["id"], router_ports)
for port in router_ports:
db_port = self._get_port(context, port["id"])
db_port["status"] = port["status"]
context.session.merge(db_port)
def _update_neutron_state(self, context, neutron_router, state):
try:
self._update_db_interfaces_state(context, neutron_router)
except Exception:
LOG.exception(_LE("Unhandled exception occurred"))
return self._set_db_router_state(context, neutron_router, state)
def _retrieve_prefix_from_port(self, context, neutron_port):
subnet_id = neutron_port["fixed_ips"][0]["subnet_id"]
subnet = utils.retrieve_subnet(context, subnet_id)
prefix = subnet["cidr"].split("/")[1]
return prefix
# L3 extension
def create_router(self, context, router):
r = router["router"]
self._get_tenant_id_for_create(context, r)
db_router = self._l3super.create_router(self, context, router)
neutron_router = self._get_router(context, db_router['id'])
gw_port = neutron_router.gw_port
# For now, only small flavor is used
utif_info = (self._plugin_support.retrieve_utif_info(context,
gw_port)
if gw_port else None)
ip_allocation_info = (utils.retrieve_ip_allocation_info(context,
gw_port)
if gw_port else None)
neutron_router = self._l3super._get_router(self, context,
neutron_router["id"])
neutron_router["status"] = p_con.Status.CREATING
self._dispatcher.dispatch_l3(
d_context=embrane_ctx.DispatcherContext(
p_con.Events.CREATE_ROUTER, neutron_router, context, None),
args=(h_con.Flavor.SMALL, utif_info, ip_allocation_info))
return self._make_router_dict(neutron_router)
def update_router(self, context, id, router):
db_router = self._l3super.update_router(self, context, id, router)
neutron_router = self._get_router(context, db_router['id'])
gw_port = neutron_router.gw_port
utif_info = (self._plugin_support.retrieve_utif_info(context,
gw_port)
if gw_port else None)
ip_allocation_info = (utils.retrieve_ip_allocation_info(context,
gw_port)
if gw_port else None)
routes_info = router["router"].get("routes")
neutron_router = self._l3super._get_router(self, context, id)
state_change = operation.Operation(
self._set_db_router_state,
args=(context, neutron_router, p_con.Status.UPDATING))
self._dispatcher.dispatch_l3(
d_context=embrane_ctx.DispatcherContext(
p_con.Events.UPDATE_ROUTER, neutron_router, context,
state_change),
args=(utif_info, ip_allocation_info, routes_info))
return self._make_router_dict(neutron_router)
def get_router(self, context, id, fields=None):
"""Ensures that id does exist in the ESM."""
neutron_router = self._get_router(context, id)
try:
if neutron_router["status"] != p_con.Status.CREATING:
self._esm_api.get_dva(id)
except h_exc.DvaNotFound:
LOG.error(_LE("The following routers have not physical match: %s"),
id)
self._set_db_router_state(context, neutron_router,
p_con.Status.ERROR)
LOG.debug("Requested router: %s", neutron_router)
return self._make_router_dict(neutron_router, fields)
def get_routers(self, context, filters=None, fields=None, sorts=None,
limit=None, marker=None, page_reverse=False):
"""Retrieves the router list defined by the incoming filters."""
router_query = self._apply_filters_to_query(
self._model_query(context, l3_db.Router),
l3_db.Router, filters)
id_list = [x["id"] for x in router_query
if x["status"] != p_con.Status.CREATING]
try:
self._esm_api.get_dvas(id_list)
except h_exc.DvaNotFound:
LOG.error(_LE("The following routers have not physical match: %s"),
repr(id_list))
error_routers = []
for id in id_list:
try:
error_routers.append(self._get_router(context, id))
except l3.RouterNotFound:
pass
for error_router in error_routers:
self._set_db_router_state(context, error_router,
p_con.Status.ERROR)
return [self._make_router_dict(router, fields)
for router in router_query]
def delete_router(self, context, id):
"""Deletes the DVA with the specific router id."""
# Copy of the parent validation code, shouldn't the base modules
# provide functions for validating operations?
device_owner_router_intf = l3_constants.DEVICE_OWNER_ROUTER_INTF
fips = self.get_floatingips_count(context.elevated(),
filters={"router_id": [id]})
if fips:
raise l3.RouterInUse(router_id=id)
device_filter = {"device_id": [id],
"device_owner": [device_owner_router_intf]}
ports = self.get_ports_count(context.elevated(),
filters=device_filter)
if ports:
raise l3.RouterInUse(router_id=id)
neutron_router = self._get_router(context, id)
state_change = operation.Operation(self._set_db_router_state,
args=(context, neutron_router,
p_con.Status.DELETING))
self._dispatcher.dispatch_l3(
d_context=embrane_ctx.DispatcherContext(
p_con.Events.DELETE_ROUTER, neutron_router, context,
state_change), args=())
LOG.debug("Deleting router=%s", neutron_router)
return neutron_router
def add_router_interface(self, context, router_id, interface_info):
"""Grows DVA interface in the specified subnet."""
neutron_router = self._get_router(context, router_id)
rport_qry = context.session.query(models_v2.Port)
ports = rport_qry.filter_by(
device_id=router_id).all()
if len(ports) >= p_con.UTIF_LIMIT:
raise neutron_exc.BadRequest(
resource=router_id,
msg=("this router doesn't support more than "
+ str(p_con.UTIF_LIMIT) + " interfaces"))
neutron_router_iface = self._l3super.add_router_interface(
self, context, router_id, interface_info)
port = self._get_port(context, neutron_router_iface["port_id"])
utif_info = self._plugin_support.retrieve_utif_info(context, port)
ip_allocation_info = utils.retrieve_ip_allocation_info(context,
port)
state_change = operation.Operation(self._set_db_router_state,
args=(context, neutron_router,
p_con.Status.UPDATING))
self._dispatcher.dispatch_l3(
d_context=embrane_ctx.DispatcherContext(
p_con.Events.GROW_ROUTER_IF, neutron_router, context,
state_change),
args=(utif_info, ip_allocation_info))
return neutron_router_iface
def remove_router_interface(self, context, router_id, interface_info):
port_id = None
if "port_id" in interface_info:
port_id = interface_info["port_id"]
elif "subnet_id" in interface_info:
subnet_id = interface_info["subnet_id"]
subnet = utils.retrieve_subnet(context, subnet_id)
rport_qry = context.session.query(models_v2.Port)
ports = rport_qry.filter_by(
device_id=router_id,
device_owner=l3_constants.DEVICE_OWNER_ROUTER_INTF,
network_id=subnet["network_id"])
for p in ports:
if p["fixed_ips"][0]["subnet_id"] == subnet_id:
port_id = p["id"]
break
neutron_router = self._get_router(context, router_id)
self._l3super.remove_router_interface(self, context, router_id,
interface_info)
state_change = operation.Operation(self._set_db_router_state,
args=(context, neutron_router,
p_con.Status.UPDATING))
self._dispatcher.dispatch_l3(
d_context=embrane_ctx.DispatcherContext(
p_con.Events.SHRINK_ROUTER_IF, neutron_router, context,
state_change),
args=(port_id,))
def create_floatingip(self, context, floatingip):
result = self._l3super.create_floatingip(
self, context, floatingip)
if result["port_id"]:
neutron_router = self._get_router(context, result["router_id"])
db_fixed_port = self._get_port(context, result["port_id"])
fixed_prefix = self._retrieve_prefix_from_port(context,
db_fixed_port)
db_floating_port = neutron_router["gw_port"]
floating_prefix = self._retrieve_prefix_from_port(
context, db_floating_port)
nat_info = utils.retrieve_nat_info(context, result,
fixed_prefix,
floating_prefix,
neutron_router)
state_change = operation.Operation(
self._set_db_router_state,
args=(context, neutron_router, p_con.Status.UPDATING))
self._dispatcher.dispatch_l3(
d_context=embrane_ctx.DispatcherContext(
p_con.Events.SET_NAT_RULE, neutron_router, context,
state_change),
args=(nat_info,))
return result
def update_floatingip(self, context, id, floatingip):
db_fip = self._l3super.get_floatingip(self, context, id)
result = self._l3super.update_floatingip(self, context, id,
floatingip)
if db_fip["port_id"] and db_fip["port_id"] != result["port_id"]:
neutron_router = self._get_router(context, db_fip["router_id"])
fip_id = db_fip["id"]
state_change = operation.Operation(
self._set_db_router_state,
args=(context, neutron_router, p_con.Status.UPDATING))
self._dispatcher.dispatch_l3(
d_context=embrane_ctx.DispatcherContext(
p_con.Events.RESET_NAT_RULE, neutron_router, context,
state_change),
args=(fip_id,))
if result["port_id"]:
neutron_router = self._get_router(context, result["router_id"])
db_fixed_port = self._get_port(context, result["port_id"])
fixed_prefix = self._retrieve_prefix_from_port(context,
db_fixed_port)
db_floating_port = neutron_router["gw_port"]
floating_prefix = self._retrieve_prefix_from_port(
context, db_floating_port)
nat_info = utils.retrieve_nat_info(context, result,
fixed_prefix,
floating_prefix,
neutron_router)
state_change = operation.Operation(
self._set_db_router_state,
args=(context, neutron_router, p_con.Status.UPDATING))
self._dispatcher.dispatch_l3(
d_context=embrane_ctx.DispatcherContext(
p_con.Events.SET_NAT_RULE, neutron_router, context,
state_change),
args=(nat_info,))
return result
def disassociate_floatingips(self, context, port_id, do_notify=True):
try:
fip_qry = context.session.query(l3_db.FloatingIP)
floating_ip = fip_qry.filter_by(fixed_port_id=port_id).one()
router_id = floating_ip["router_id"]
except exc.NoResultFound:
return
router_ids = self._l3super.disassociate_floatingips(
self, context, port_id, do_notify=do_notify)
if router_id:
neutron_router = self._get_router(context, router_id)
fip_id = floating_ip["id"]
state_change = operation.Operation(
self._set_db_router_state,
args=(context, neutron_router, p_con.Status.UPDATING))
self._dispatcher.dispatch_l3(
d_context=embrane_ctx.DispatcherContext(
p_con.Events.RESET_NAT_RULE, neutron_router, context,
state_change),
args=(fip_id,))
return router_ids

View File

@ -1,45 +0,0 @@
# Copyright 2013 Embrane, Inc.
# 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.
from oslo_config import cfg
heleos_opts = [
cfg.StrOpt('esm_mgmt',
help=_('ESM management root address')),
cfg.StrOpt('admin_username', default='admin',
help=_('ESM admin username.')),
cfg.StrOpt('admin_password',
secret=True,
help=_('ESM admin password.')),
cfg.StrOpt('router_image',
help=_('Router image id (Embrane FW/VPN)')),
cfg.StrOpt('inband_id',
help=_('In band Security Zone id')),
cfg.StrOpt('oob_id',
help=_('Out of band Security Zone id')),
cfg.StrOpt('mgmt_id',
help=_('Management Security Zone id')),
cfg.StrOpt('dummy_utif_id',
help=_('Dummy user traffic Security Zone id')),
cfg.StrOpt('resource_pool_id', default='default',
help=_('Shared resource pool id')),
cfg.BoolOpt('async_requests', default=True,
help=_('Define if the requests have '
'run asynchronously or not')),
]
cfg.CONF.register_opts(heleos_opts, "heleos")

View File

@ -1,68 +0,0 @@
# Copyright 2013 Embrane, Inc.
# 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.
from heleosapi import exceptions as h_exc
from neutron.plugins.common import constants
# Router specific constants
UTIF_LIMIT = 7
QUEUE_TIMEOUT = 300
class Status(object):
# Transient
CREATING = constants.PENDING_CREATE
UPDATING = constants.PENDING_UPDATE
DELETING = constants.PENDING_DELETE
# Final
ACTIVE = constants.ACTIVE
ERROR = constants.ERROR
READY = constants.INACTIVE
DELETED = "DELETED" # not visible
class Events(object):
CREATE_ROUTER = "create_router"
UPDATE_ROUTER = "update_router"
DELETE_ROUTER = "delete_router"
GROW_ROUTER_IF = "grow_router_if"
SHRINK_ROUTER_IF = "shrink_router_if"
SET_NAT_RULE = "set_nat_rule"
RESET_NAT_RULE = "reset_nat_rule"
_DVA_PENDING_ERROR_MSG = _("Dva is pending for the following reason: %s")
_DVA_NOT_FOUNT_ERROR_MSG = _("Dva can't be found to execute the operation, "
"probably was cancelled through the heleos UI")
_DVA_BROKEN_ERROR_MSG = _("Dva seems to be broken for reason %s")
_DVA_BROKEN_INTERFACE_ERROR_MSG = _("Dva interface seems to be broken "
"for reason %s")
_DVA_CREATION_FAILED_ERROR_MSG = _("Dva creation failed reason %s")
_DVA_CREATION_PENDING_ERROR_MSG = _("Dva creation is in pending state "
"for reason %s")
_CFG_FAILED_ERROR_MSG = _("Dva configuration failed for reason %s")
_DVA_DEL_FAILED_ERROR_MSG = _("Failed to delete the backend "
"router for reason %s. Please remove "
"it manually through the heleos UI")
error_map = {h_exc.PendingDva: _DVA_PENDING_ERROR_MSG,
h_exc.DvaNotFound: _DVA_NOT_FOUNT_ERROR_MSG,
h_exc.BrokenDva: _DVA_BROKEN_ERROR_MSG,
h_exc.BrokenInterface: _DVA_BROKEN_INTERFACE_ERROR_MSG,
h_exc.DvaCreationFailed: _DVA_CREATION_FAILED_ERROR_MSG,
h_exc.DvaCreationPending: _DVA_CREATION_PENDING_ERROR_MSG,
h_exc.ConfigurationFailed: _CFG_FAILED_ERROR_MSG,
h_exc.DvaDeleteFailed: _DVA_DEL_FAILED_ERROR_MSG}

View File

@ -1,36 +0,0 @@
# Copyright 2013 Embrane, Inc.
# 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.
class DispatcherContext(object):
def __init__(self, event, item, neutron_context, chain=None):
self.event = event
self.item = item
self.n_context = neutron_context
self.chain = chain
class OperationContext(DispatcherContext):
"""Operational context.
contains all the parameters needed to execute a status aware operation
"""
def __init__(self, event, context, item, chain, function, args, kwargs):
super(OperationContext, self).__init__(event, item, context, chain)
self.function = function
self.args = args
self.kwargs = kwargs

View File

@ -1,24 +0,0 @@
# Copyright 2013 Embrane, Inc.
# 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.
from neutron.common import exceptions as neutron_exec
class EmbranePluginException(neutron_exec.NeutronException):
message = _("An unexpected error occurred:%(err_msg)s")
class UnsupportedException(EmbranePluginException):
message = _("%(err_msg)s")

View File

@ -1,47 +0,0 @@
# Copyright 2013 Embrane, Inc.
# 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.
class Operation(object):
"""Defines a series of operations which shall be executed in order.
the operations expected are procedures, return values are discarded
"""
def __init__(self, procedure, args=(), kwargs={}, nextop=None):
self._procedure = procedure
self.args = args[:]
self.kwargs = dict(kwargs)
self.nextop = nextop
def execute(self):
args = self.args
self._procedure(*args, **self.kwargs)
return self.nextop
def execute_all(self):
nextop = self.execute()
while nextop:
nextop = self.execute_all()
def has_next(self):
return self.nextop is not None
def add_bottom_operation(self, operation):
op = self
while op.has_next():
op = op.nextop
op.nextop = operation

View File

@ -1,70 +0,0 @@
# Copyright 2013 Embrane, Inc.
# 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.
from heleosapi import info as h_info
from oslo_log import log as logging
from neutron.common import constants
from neutron.db import models_v2
from neutron.i18n import _LI
LOG = logging.getLogger(__name__)
def set_db_item_state(context, neutron_item, new_state):
with context.session.begin(subtransactions=True):
if neutron_item["status"] != new_state:
neutron_item["status"] = new_state
context.session.merge(neutron_item)
def retrieve_subnet(context, subnet_id):
return (context.session.query(
models_v2.Subnet).filter(models_v2.Subnet.id == subnet_id).one())
def retrieve_ip_allocation_info(context, neutron_port):
"""Retrieves ip allocation info for a specific port if any."""
try:
subnet_id = neutron_port["fixed_ips"][0]["subnet_id"]
except (KeyError, IndexError):
LOG.info(_LI("No ip allocation set"))
return
subnet = retrieve_subnet(context, subnet_id)
allocated_ip = neutron_port["fixed_ips"][0]["ip_address"]
is_gw_port = (neutron_port["device_owner"] ==
constants.DEVICE_OWNER_ROUTER_GW)
gateway_ip = subnet["gateway_ip"]
ip_allocation_info = h_info.IpAllocationInfo(
is_gw=is_gw_port,
ip_version=subnet["ip_version"],
prefix=subnet["cidr"].split("/")[1],
ip_address=allocated_ip,
port_id=neutron_port["id"],
gateway_ip=gateway_ip)
return ip_allocation_info
def retrieve_nat_info(context, fip, fixed_prefix, floating_prefix, router):
nat_info = h_info.NatInfo(source_address=fip["floating_ip_address"],
source_prefix=floating_prefix,
destination_address=fip["fixed_ip_address"],
destination_prefix=fixed_prefix,
floating_ip_id=fip["id"],
fixed_port_id=fip["port_id"])
return nat_info

View File

@ -1,20 +0,0 @@
# Copyright 2013 Embrane, Inc.
# 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.
from neutron.db import db_base_plugin_v2
class FakeL2Plugin(db_base_plugin_v2.NeutronDbPluginV2):
supported_extension_aliases = []

View File

@ -1,41 +0,0 @@
# Copyright 2013 Embrane, Inc.
# 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.
from heleosapi import info as h_info
from neutron.common import constants
from neutron import manager
from neutron.plugins.embrane.l2base import support_base as base
class FakePluginSupport(base.SupportBase):
def __init__(self):
super(FakePluginSupport, self).__init__()
def retrieve_utif_info(self, context, neutron_port):
plugin = manager.NeutronManager.get_plugin()
network_id = neutron_port["network_id"]
network = plugin._get_network(context, network_id)
is_gw = (neutron_port["device_owner"] ==
constants.DEVICE_OWNER_ROUTER_GW)
result = h_info.UtifInfo(vlan=0,
network_name=network["name"],
network_id=network["id"],
is_gw=is_gw,
owner_tenant=network["tenant_id"],
port_id=neutron_port["id"],
mac_address=neutron_port["mac_address"])
return result

View File

@ -1,52 +0,0 @@
# Copyright 2014 Embrane, Inc.
# 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.
from heleosapi import info as h_info
from neutron.common import constants
from neutron import manager
from neutron.plugins.embrane.l2base import support_base as base
from neutron.plugins.embrane.l2base import support_exceptions as exc
class Ml2Support(base.SupportBase):
"""Modular Layer 2 plugin support.
Obtains the information needed to build the user security zones.
"""
def __init__(self):
super(Ml2Support, self).__init__()
def retrieve_utif_info(self, context, neutron_port):
plugin = manager.NeutronManager.get_plugin()
network = plugin.get_network(
context, neutron_port['network_id'])
is_gw = (neutron_port["device_owner"] ==
constants.DEVICE_OWNER_ROUTER_GW)
network_type = network.get('provider:network_type')
if network_type != 'vlan':
raise exc.UtifInfoError(
err_msg=_("Network type %s not supported. Please be sure "
"that tenant_network_type is vlan") % network_type)
result = h_info.UtifInfo(network.get('provider:segmentation_id'),
network['name'],
network['id'],
is_gw,
network['tenant_id'],
neutron_port['id'],
neutron_port['mac_address'])
return result

View File

@ -1,46 +0,0 @@
# Copyright 2013 Embrane, Inc.
# 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 abc
import six
@six.add_metaclass(abc.ABCMeta)
class SupportBase(object):
"""abstract support class.
Defines the methods a plugin support should implement to be used as
the L2 base for Embrane plugin.
"""
@abc.abstractmethod
def __init__(self):
pass
@abc.abstractmethod
def retrieve_utif_info(self, context, neutron_port=None, network=None):
"""Retrieve specific network info.
each plugin support, querying its own DB, can collect all the
information needed by the ESM in order to create the
user traffic security zone.
:param interface_info: the foo parameter
:param context: neutron request context
:returns: heleosapi.info.UtifInfo -- specific network info
:raises: UtifInfoError
"""

View File

@ -1,30 +0,0 @@
# Copyright 2013 Embrane, Inc.
# 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.
from neutron.db import extraroute_db
from neutron.plugins.embrane import base_plugin as base
from neutron.plugins.embrane.l2base.fake import fake_l2_plugin as l2
from neutron.plugins.embrane.l2base.fake import fakeplugin_support as sup
class EmbraneFakePlugin(base.EmbranePlugin, extraroute_db.ExtraRoute_db_mixin,
l2.FakeL2Plugin):
_plugin_support = sup.FakePluginSupport()
def __init__(self):
'''First run plugin specific initialization, then Embrane's.'''
self.supported_extension_aliases += ["extraroute", "router"]
l2.FakeL2Plugin.__init__(self)
self._run_embrane_config()

View File

@ -1,41 +0,0 @@
# Copyright 2014 Embrane, Inc.
# 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.
from neutron.db import extraroute_db
from neutron.db import l3_dvr_db
from neutron.db import l3_gwmode_db
from neutron.plugins.embrane import base_plugin as base
from neutron.plugins.embrane.l2base.ml2 import ml2_support
from neutron.plugins.ml2 import plugin as l2
class EmbraneMl2Plugin(base.EmbranePlugin, l2.Ml2Plugin,
l3_dvr_db.L3_NAT_with_dvr_db_mixin,
l3_gwmode_db.L3_NAT_db_mixin,
extraroute_db.ExtraRoute_db_mixin):
'''EmbraneMl2Plugin.
This plugin uses Modular Layer 2 plugin for providing L2 networks
and the base EmbranePlugin for L3.
'''
_plugin_support = ml2_support.Ml2Support()
def __init__(self):
'''First run plugin specific initialization, then Embrane's.'''
self._supported_extension_aliases.extend(["router", "extraroute",
"ext-gw-mode"])
l2.Ml2Plugin.__init__(self)
self._run_embrane_config()

View File

@ -1,30 +0,0 @@
# Copyright 2013 Embrane, Inc.
# 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.
from oslo_config import cfg
from neutron.plugins.embrane.common import config # noqa
from neutron.tests.unit.extensions import test_extraroute as extraroute_test
PLUGIN_NAME = ('neutron.plugins.embrane.plugins.embrane_fake_plugin.'
'EmbraneFakePlugin')
class TestEmbraneL3NatDBTestCase(extraroute_test.ExtraRouteDBIntTestCase):
_plugin_name = PLUGIN_NAME
def setUp(self):
cfg.CONF.set_override('admin_password', "admin123", 'heleos')
super(TestEmbraneL3NatDBTestCase, self).setUp()

View File

@ -1,76 +0,0 @@
# Copyright 2013 Embrane, Inc.
# 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 sys
import mock
from oslo_config import cfg
from neutron.plugins.embrane.common import config # noqa
from neutron.tests.unit.db import test_db_base_plugin_v2 as test_plugin
PLUGIN_NAME = ('neutron.plugins.embrane.plugins.embrane_fake_plugin.'
'EmbraneFakePlugin')
class EmbranePluginV2TestCase(test_plugin.NeutronDbPluginV2TestCase):
_plugin_name = PLUGIN_NAME
def setUp(self):
cfg.CONF.set_override('admin_password', "admin123", 'heleos')
p = mock.patch.dict(sys.modules, {'heleosapi': mock.Mock()})
p.start()
# dict patches must be explicitly stopped
self.addCleanup(p.stop)
super(EmbranePluginV2TestCase, self).setUp(self._plugin_name)
class TestEmbraneBasicGet(test_plugin.TestBasicGet, EmbranePluginV2TestCase):
pass
class TestEmbraneV2HTTPResponse(test_plugin.TestV2HTTPResponse,
EmbranePluginV2TestCase):
pass
class TestEmbranePortsV2(test_plugin.TestPortsV2, EmbranePluginV2TestCase):
def test_create_ports_bulk_emulated_plugin_failure(self):
self.skip("Temporary skipping due to incompatibility with the"
" plugin dynamic class type")
def test_recycle_expired_previously_run_within_context(self):
self.skip("Temporary skipping due to incompatibility with the"
" plugin dynamic class type")
def test_recycle_held_ip_address(self):
self.skip("Temporary skipping due to incompatibility with the"
" plugin dynamic class type")
class TestEmbraneNetworksV2(test_plugin.TestNetworksV2,
EmbranePluginV2TestCase):
def test_create_networks_bulk_emulated_plugin_failure(self):
self.skip("Temporary skipping due to incompatibility with the"
" plugin dynamic class type")
class TestEmbraneSubnetsV2(test_plugin.TestSubnetsV2,
EmbranePluginV2TestCase):
def test_create_subnets_bulk_emulated_plugin_failure(self):
self.skip("Temporary skipping due to incompatibility with the"
" plugin dynamic class type")

View File

@ -54,7 +54,6 @@ data_files =
etc/neutron/plugins/brocade/vyatta = etc/neutron/plugins/brocade/vyatta/vrouter.ini
etc/neutron/plugins/cisco =
etc/neutron/plugins/cisco/cisco_vpn_agent.ini
etc/neutron/plugins/embrane = etc/neutron/plugins/embrane/heleos_conf.ini
etc/neutron/plugins/ml2 =
etc/neutron/plugins/bigswitch/restproxy.ini
etc/neutron/plugins/ml2/linuxbridge_agent.ini
@ -104,7 +103,6 @@ console_scripts =
neutron.core_plugins =
bigswitch = neutron.plugins.bigswitch.plugin:NeutronRestProxyV2
brocade = neutron.plugins.brocade.NeutronPlugin:BrocadePluginV2
embrane = neutron.plugins.embrane.plugins.embrane_ml2_plugin:EmbraneMl2Plugin
ml2 = neutron.plugins.ml2.plugin:Ml2Plugin
nuage = neutron.plugins.nuage.plugin:NuagePlugin
neutron.service_plugins =

View File

@ -14,7 +14,6 @@ ignore_regexes=(
# The following vendor plugins are not required to confrm to the
# structural requirements.
"^plugins/brocade.*$"
"^plugins/embrane.*$"
"^plugins/ibm.*$"
# The following open source plugin tests are not actually unit
# tests and are ignored pending their relocation to the functional