Embrane Neutron Plugin

Implements blueprint embrane-neutron-plugin

This commit implements the main Embrane plugin, which covers
route and extratoute extensions.

The plugin will rely on existing plugins for leveraging L2 networks,
for now it only supports OpenVSwitch and vlan networks, but more
supports are to come if the model is approved (or a different one
is suggested).

Change-Id: I90aff8ec4324bd3a7c59c411374db6a118d1a72b
This commit is contained in:
Ivar Lazzaro 2013-07-22 13:32:31 -07:00
parent 5fec791ace
commit 3c95d18311
30 changed files with 1631 additions and 0 deletions

View File

@ -0,0 +1,41 @@
[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

@ -0,0 +1,9 @@
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

@ -0,0 +1,18 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# 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.
#
# @author: Ivar Lazzaro, Embrane, Inc.

View File

@ -0,0 +1,18 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# 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.
#
# @author: Ivar Lazzaro, Embrane, Inc.

View File

@ -0,0 +1,145 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# 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.
#
# @author: Ivar Lazzaro, Embrane, Inc.
from eventlet import greenthread
from eventlet import queue
from heleosapi import constants as h_con
from heleosapi import exceptions as h_exc
from neutron.openstack.common import log as logging
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
from neutron.plugins.embrane.common import exceptions as plugin_exc
LOG = logging.getLogger(__name__)
def _validate_operation(event, status, item_id):
if status and event not in p_con.operation_filter[status]:
raise plugin_exc.StateConstraintException(operation=event,
dva_id=item_id, state=status)
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
q_context = d_context.q_context
chain = d_context.chain
item_id = item["id"]
# First round validation (Controller level)
_validate_operation(event, item["status"], 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].put(
ctx.OperationContext(event, q_context, item, chain, f,
args, kwargs))
if first_run:
t = greenthread.spawn(self._consume_l3,
item_id,
self.sync_items[item_id],
self._plugin)
if not self._async:
t.wait()
def _consume_l3(self, sync_item, sync_queue, plugin):
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:
operation_context = sync_queue.get(
timeout=p_con.QUEUE_TIMEOUT)
except queue.Empty:
del self.sync_items[sync_item]
return
# Second round validation (enqueued level)
_validate_operation(operation_context.event,
current_state,
operation_context.item["id"])
# 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.q_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.message)
transient_state = p_con.Status.ERROR
except h_exc.DvaDeleteFailed as ex:
LOG.warning(p_con.error_map[type(ex)] % ex.message)
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.q_context,
operation_context.item["id"])
# Error state cannot be reverted
elif current_state != p_con.Status.ERROR:
current_state = plugin._update_neutron_state(
operation_context.q_context,
operation_context.item,
transient_state)
except plugin_exc.StateConstraintException as e:
LOG.error(_("%s"), e.message)
except Exception:
LOG.exception(_("Unhandled exception occurred"))

View File

@ -0,0 +1,18 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# 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.
#
# @author: Ivar Lazzaro, Embrane, Inc.

View File

@ -0,0 +1,154 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# 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.
#
# @author: Ivar Lazzaro, Embrane, Inc.
from functools import wraps
from heleosapi import exceptions as h_exc
from neutron.openstack.common import log as logging
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)
@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=ex.message)
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(_("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=ex.message)
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(_("Interface %s not found in the heleos back-end,"
"likely already deleted"), port_id)
except h_exc.PreliminaryOperationsFailed as ex:
raise h_exc.BrokenInterface(err_msg=ex.message)
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

@ -0,0 +1,364 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# 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.
#
# @author: Ivar Lazzaro, Embrane, Inc.
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 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.openstack.common import log as logging
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 exceptions as c_exc
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(_("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 = self._get_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)
with context.session.begin(subtransactions=True):
neutron_router = self._l3super.create_router(self, context, router)
network_id = None
gw_port = None
ext_gw_info = neutron_router.get(l3_db.EXTERNAL_GW_INFO)
if ext_gw_info:
network_id = ext_gw_info.get("network_id")
if network_id:
gw_ports = self.get_ports(
context,
{"device_id": [id],
"device_owner": ["network:router_gateway"]})
if len(gw_ports) != 1:
raise c_exc.EmbranePluginException(
err_msg=_("there must be only one gateway port "
"per router at once"))
gw_port = gw_ports[0]
# For now, only small flavor is used
utif_info = (self._plugin_support.retrieve_utif_info(context,
gw_port)
if network_id else None)
ip_allocation_info = (utils.retrieve_ip_allocation_info(self,
context,
gw_port)
if network_id 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):
with context.session.begin(subtransactions=True):
db_router = self._l3super.update_router(self, context, id, router)
gw_port = None
ext_gw_info = db_router.get(l3_db.EXTERNAL_GW_INFO)
if ext_gw_info:
ext_gw_info = db_router[l3_db.EXTERNAL_GW_INFO]
network_id = (ext_gw_info.get("network_id")
if ext_gw_info else None)
if network_id:
gw_ports = self.get_ports(
context,
{"device_id": [id],
"device_owner": ["network:router_gateway"]})
if len(gw_ports) != 1:
raise c_exc.EmbranePluginException(
err_msg=_("there must be only one gateway port"
" per router at once"))
gw_port = gw_ports[0]
utif_info = (self._plugin_support.retrieve_utif_info(context,
gw_port)
if gw_port else None)
ip_allocation_info = (utils.retrieve_ip_allocation_info(self,
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(_("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(_("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?
with context.session.begin(subtransactions=True):
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."""
with context.session.begin(subtransactions=True):
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(self,
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 = self._get_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 update_floatingip(self, context, id, floatingip):
with context.session.begin(subtransactions=True):
db_fip = self._l3super.get_floatingip(self, context, id)
result = self._l3super.update_floatingip(self, context, id,
floatingip)
if db_fip["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 floatingip["floatingip"]["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

View File

@ -0,0 +1,18 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# 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.
#
# @author: Ivar Lazzaro, Embrane, Inc.

View File

@ -0,0 +1,50 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# 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.
#
# @author: Ivar Lazzaro, Embrane, Inc.
from oslo.config import cfg
heleos_opts = [
cfg.StrOpt('esm_mgmt',
default=None,
help=_('ESM management root address')),
cfg.StrOpt('admin_username', default='admin',
help=_('ESM admin username.')),
cfg.StrOpt('admin_password', default=None,
secret=True,
help=_('ESM admin password.')),
cfg.StrOpt('router_image', default=None,
help=_('Router image id (Embrane FW/VPN)')),
cfg.StrOpt('inband_id', default=None,
help=_('In band Security Zone id')),
cfg.StrOpt('oob_id', default=None,
help=_('Out of band Security Zone id')),
cfg.StrOpt('mgmt_id', default=None,
help=_('Management Security Zone id')),
cfg.StrOpt('dummy_utif_id', default=None,
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

@ -0,0 +1,84 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# 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.
#
# @author: Ivar Lazzaro, Embrane, Inc.
from heleosapi import exceptions as h_exc
from neutron.plugins.common import constants
# Router specific constants
UTIF_LIMIT = 7
QUEUE_TIMEOUT = 300
class Status:
# 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:
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"
operation_filter = {
Status.ACTIVE: [Events.DELETE_ROUTER, Events.GROW_ROUTER_IF,
Events.SHRINK_ROUTER_IF, Events.UPDATE_ROUTER,
Events.SET_NAT_RULE, Events.RESET_NAT_RULE],
Status.READY: [Events.DELETE_ROUTER, Events.GROW_ROUTER_IF,
Events.SHRINK_ROUTER_IF, Events.UPDATE_ROUTER],
Status.ERROR: [Events.DELETE_ROUTER, Events.SHRINK_ROUTER_IF],
Status.UPDATING: [Events.DELETE_ROUTER, Events.SHRINK_ROUTER_IF,
Events.RESET_NAT_RULE],
Status.CREATING: [Events.DELETE_ROUTER, Events.CREATE_ROUTER],
Status.DELETING: [Events.DELETE_ROUTER]}
_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

@ -0,0 +1,40 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# 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.
#
# @author: Ivar Lazzaro, Embrane, Inc.
class DispatcherContext(object):
def __init__(self, event, item, neutron_context, chain=None):
self.event = event
self.item = item
self.q_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

@ -0,0 +1,35 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# 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.
#
# @author: Ivar Lazzaro, Embrane, Inc.
from neutron.common import exceptions as neutron_exec
class EmbranePluginException(neutron_exec.NeutronException):
message = _("An unexpected error occurred:%(err_msg)s")
# Not permitted operation
class NonPermitted(neutron_exec.BadRequest):
pass
class StateConstraintException(NonPermitted):
message = _("Operation not permitted due to state constraint violation:"
"%(operation)s not allowed for DVA %(dva_id)s in state "
" %(state)s")

View File

@ -0,0 +1,51 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# 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.
#
# @author: Ivar Lazzaro, Embrane, Inc.
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

@ -0,0 +1,65 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# 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.
#
# @author: Ivar Lazzaro, Embrane, Inc.
from heleosapi import info as h_info
from neutron.openstack.common import log as logging
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_ip_allocation_info(l2_plugin, 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(_("No ip allocation set"))
return
subnet = l2_plugin._get_subnet(context, subnet_id)
allocated_ip = neutron_port["fixed_ips"][0]["ip_address"]
is_gw_port = neutron_port["device_owner"] == "network:router_gateway"
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

@ -0,0 +1,18 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# 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.
#
# @author: Ivar Lazzaro, Embrane, Inc.

View File

@ -0,0 +1,18 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# 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.
#
# @author: Ivar Lazzaro, Embrane, Inc.

View File

@ -0,0 +1,24 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# 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.
#
# @author: Ivar Lazzaro, Embrane, Inc.
from neutron.db import db_base_plugin_v2
class FakeL2Plugin(db_base_plugin_v2.NeutronDbPluginV2):
supported_extension_aliases = []

View File

@ -0,0 +1,43 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# 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.
#
# @author: Ivar Lazzaro, Embrane, Inc.
from heleosapi import info as h_info
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"] == "network:router_gateway"
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

@ -0,0 +1,18 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# 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.
#
# @author: Ivar Lazzaro, Embrane, Inc.

View File

@ -0,0 +1,56 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# 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.
#
# @author: Ivar Lazzaro, Embrane, Inc.
from heleosapi import info as h_info
from neutron import manager
from neutron.plugins.embrane.l2base import support_base as base
from neutron.plugins.embrane.l2base import support_exceptions as exc
from neutron.plugins.openvswitch import ovs_db_v2
class OpenvswitchSupport(base.SupportBase):
"""OpenVSwitch plugin support.
Obtains the informations needed to build the user security zones
"""
def __init__(self):
super(OpenvswitchSupport, self).__init__()
def retrieve_utif_info(self, context, neutron_port):
plugin = manager.NeutronManager.get_plugin()
session = context.session
network_id = neutron_port["network_id"]
network_binding = ovs_db_v2.get_network_binding(session, network_id)
if not network_binding["segmentation_id"]:
raise exc.UtifInfoError(
err_msg=_("No segmentation_id found for the network, "
"please be sure that tenant_network_type is vlan"))
network = plugin._get_network(context, network_id)
is_gw = neutron_port["device_owner"] == "network:router_gateway"
result = h_info.UtifInfo(vlan=network_binding["segmentation_id"],
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

@ -0,0 +1,48 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# 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.
#
# @author: Ivar Lazzaro, Embrane, Inc.
import abc
class SupportBase(object):
"""abstract support class.
Defines the methods a plugin support should implement to be used as
the L2 base for Embrane plugin.
"""
__metaclass__ = abc.ABCMeta
@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

@ -0,0 +1,25 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# 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.
#
# @author: Ivar Lazzaro, Embrane, Inc.
from neutron.plugins.embrane.common import exceptions as embrane_exc
class UtifInfoError(embrane_exc.EmbranePluginException):
message = _("cannot retrieve utif info for the following reason: "
"%(err_msg)s")

View File

@ -0,0 +1,18 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# 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.
#
# @author: Ivar Lazzaro, Embrane, Inc.

View File

@ -0,0 +1,34 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# 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.
#
# @author: Ivar Lazzaro, Embrane, Inc.
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

@ -0,0 +1,37 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# 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.
#
# @author: Ivar Lazzaro, Embrane, Inc.
from neutron.plugins.embrane import base_plugin as base
from neutron.plugins.embrane.l2base.openvswitch import openvswitch_support
from neutron.plugins.openvswitch import ovs_neutron_plugin as l2
class EmbraneOvsPlugin(base.EmbranePlugin, l2.OVSNeutronPluginV2):
'''EmbraneOvsPlugin.
This plugin uses OpenVSwitch specific L2 plugin for providing L2 networks
and the base EmbranePlugin for L3.
'''
_plugin_support = openvswitch_support.OpenvswitchSupport
def __init__(self):
'''First run plugin specific initialization, then Embrane's.'''
l2.OVSNeutronPluginV2.__init__(self)
self._run_embrane_config()

View File

@ -0,0 +1,18 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# 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.
#
# @author: Ivar Lazzaro, Embrane, Inc.

View File

@ -0,0 +1,37 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# 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.
#
# @author: Ivar Lazzaro, Embrane, Inc.
import sys
import mock
from oslo.config import cfg
from neutron.plugins.embrane.common import config # noqa
from neutron.tests import base
# Need to mock heleosapi.
sys.modules["heleosapi"] = mock.Mock()
class ConfigurationTest(base.BaseTestCase):
def test_defaults(self):
self.assertEqual('admin', cfg.CONF.heleos.admin_username)
self.assertEqual('default', cfg.CONF.heleos.resource_pool_id)
self.assertTrue(cfg.CONF.heleos.async_requests)

View File

@ -0,0 +1,46 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# 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.
#
# @author: Ivar Lazzaro, Embrane, Inc.
import sys
import mock
from oslo.config import cfg
from neutron.db import api as db
from neutron.plugins.embrane.common import config # noqa
from neutron.tests.unit import test_extension_extraroute as extraroute_test
from neutron.tests.unit import test_l3_plugin as router_test
PLUGIN_NAME = ('neutron.plugins.embrane.plugins.embrane_fake_plugin.'
'EmbraneFakePlugin')
sys.modules["heleosapi"] = mock.Mock()
class TestEmbraneL3NatDBTestCase(router_test.L3NatDBTestCase):
_plugin_name = PLUGIN_NAME
def setUp(self):
cfg.CONF.set_override('admin_password', "admin123", 'heleos')
self.addCleanup(cfg.CONF.reset)
self.addCleanup(db.clear_db)
super(TestEmbraneL3NatDBTestCase, self).setUp()
class ExtraRouteDBTestCase(extraroute_test.ExtraRouteDBTestCase):
_plugin_name = PLUGIN_NAME

View File

@ -0,0 +1,81 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# 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.
#
# @author: Ivar Lazzaro, Embrane, Inc.
import sys
import mock
from oslo.config import cfg
from neutron.db import api as db
from neutron.plugins.embrane.common import config # noqa
from neutron.tests.unit import test_db_plugin as test_plugin
PLUGIN_NAME = ('neutron.plugins.embrane.plugins.embrane_fake_plugin.'
'EmbraneFakePlugin')
sys.modules["heleosapi"] = mock.Mock()
class EmbranePluginV2TestCase(test_plugin.NeutronDbPluginV2TestCase):
_plugin_name = PLUGIN_NAME
def setUp(self):
cfg.CONF.set_override('admin_password', "admin123", 'heleos')
self.addCleanup(cfg.CONF.reset)
self.addCleanup(db.clear_db)
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")