--- /dev/null
+[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=
--- /dev/null
+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
\ No newline at end of file
--- /dev/null
+# 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.
--- /dev/null
+# 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.
--- /dev/null
+# 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"))
--- /dev/null
+# 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.
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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.
--- /dev/null
+# 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")
--- /dev/null
+# 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}
--- /dev/null
+# 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
--- /dev/null
+# 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")
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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.
--- /dev/null
+# 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.
--- /dev/null
+# 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 = []
--- /dev/null
+# 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
--- /dev/null
+# 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.
--- /dev/null
+# 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
--- /dev/null
+# 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
+ """
--- /dev/null
+# 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")
--- /dev/null
+# 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.
--- /dev/null
+# 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()
--- /dev/null
+# 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()
--- /dev/null
+# 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.
--- /dev/null
+# 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)
--- /dev/null
+# 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
--- /dev/null
+# 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")