Merge "Embrane Neutron Plugin"
This commit is contained in:
commit
5bbb0f7ce4
41
etc/neutron/plugins/embrane/heleos_conf.ini
Normal file
41
etc/neutron/plugins/embrane/heleos_conf.ini
Normal 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=
|
9
neutron/plugins/embrane/README
Normal file
9
neutron/plugins/embrane/README
Normal 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
|
18
neutron/plugins/embrane/__init__.py
Normal file
18
neutron/plugins/embrane/__init__.py
Normal 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.
|
18
neutron/plugins/embrane/agent/__init__.py
Normal file
18
neutron/plugins/embrane/agent/__init__.py
Normal 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.
|
145
neutron/plugins/embrane/agent/dispatcher.py
Normal file
145
neutron/plugins/embrane/agent/dispatcher.py
Normal 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"))
|
18
neutron/plugins/embrane/agent/operations/__init__.py
Normal file
18
neutron/plugins/embrane/agent/operations/__init__.py
Normal 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.
|
154
neutron/plugins/embrane/agent/operations/router_operations.py
Normal file
154
neutron/plugins/embrane/agent/operations/router_operations.py
Normal 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
|
364
neutron/plugins/embrane/base_plugin.py
Normal file
364
neutron/plugins/embrane/base_plugin.py
Normal 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
|
18
neutron/plugins/embrane/common/__init__.py
Normal file
18
neutron/plugins/embrane/common/__init__.py
Normal 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.
|
50
neutron/plugins/embrane/common/config.py
Normal file
50
neutron/plugins/embrane/common/config.py
Normal 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")
|
84
neutron/plugins/embrane/common/constants.py
Normal file
84
neutron/plugins/embrane/common/constants.py
Normal 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}
|
40
neutron/plugins/embrane/common/contexts.py
Normal file
40
neutron/plugins/embrane/common/contexts.py
Normal 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
|
35
neutron/plugins/embrane/common/exceptions.py
Normal file
35
neutron/plugins/embrane/common/exceptions.py
Normal 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")
|
51
neutron/plugins/embrane/common/operation.py
Normal file
51
neutron/plugins/embrane/common/operation.py
Normal 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
|
65
neutron/plugins/embrane/common/utils.py
Normal file
65
neutron/plugins/embrane/common/utils.py
Normal 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
|
18
neutron/plugins/embrane/l2base/__init__.py
Normal file
18
neutron/plugins/embrane/l2base/__init__.py
Normal 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.
|
18
neutron/plugins/embrane/l2base/fake/__init__.py
Normal file
18
neutron/plugins/embrane/l2base/fake/__init__.py
Normal 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.
|
24
neutron/plugins/embrane/l2base/fake/fake_l2_plugin.py
Normal file
24
neutron/plugins/embrane/l2base/fake/fake_l2_plugin.py
Normal 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 = []
|
43
neutron/plugins/embrane/l2base/fake/fakeplugin_support.py
Normal file
43
neutron/plugins/embrane/l2base/fake/fakeplugin_support.py
Normal 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
|
18
neutron/plugins/embrane/l2base/openvswitch/__init__.py
Normal file
18
neutron/plugins/embrane/l2base/openvswitch/__init__.py
Normal 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.
|
@ -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
|
48
neutron/plugins/embrane/l2base/support_base.py
Normal file
48
neutron/plugins/embrane/l2base/support_base.py
Normal 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
|
||||||
|
"""
|
25
neutron/plugins/embrane/l2base/support_exceptions.py
Normal file
25
neutron/plugins/embrane/l2base/support_exceptions.py
Normal 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")
|
18
neutron/plugins/embrane/plugins/__init__.py
Normal file
18
neutron/plugins/embrane/plugins/__init__.py
Normal 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.
|
34
neutron/plugins/embrane/plugins/embrane_fake_plugin.py
Normal file
34
neutron/plugins/embrane/plugins/embrane_fake_plugin.py
Normal 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()
|
37
neutron/plugins/embrane/plugins/embrane_ovs_plugin.py
Normal file
37
neutron/plugins/embrane/plugins/embrane_ovs_plugin.py
Normal 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()
|
18
neutron/tests/unit/embrane/__init__.py
Normal file
18
neutron/tests/unit/embrane/__init__.py
Normal 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.
|
37
neutron/tests/unit/embrane/test_embrane_defaults.py
Normal file
37
neutron/tests/unit/embrane/test_embrane_defaults.py
Normal 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)
|
46
neutron/tests/unit/embrane/test_embrane_l3_plugin.py
Normal file
46
neutron/tests/unit/embrane/test_embrane_l3_plugin.py
Normal 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
|
81
neutron/tests/unit/embrane/test_embrane_neutron_plugin.py
Normal file
81
neutron/tests/unit/embrane/test_embrane_neutron_plugin.py
Normal 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")
|
Loading…
Reference in New Issue
Block a user