Set Up the neutron l3 sdn addon using neutron as library
L3 service plugin L3 Controller Agent + OpenFlow controller Change-Id: Icdc91c0c9721e14e0e85ba0b4736d7c5b979bdbd
This commit is contained in:
parent
bda541f819
commit
2167a8bafe
|
@ -0,0 +1,33 @@
|
|||
|
||||
# Copyright (c) 2014 OpenStack Foundation.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
import abc
|
||||
import six
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class ControllerBase(object):
|
||||
"""Abstract Controller object ."""
|
||||
|
||||
def __init__(self, conf, controllertype):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def sync_port(self, router):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def sync_router(self, router):
|
||||
pass
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,64 @@
|
|||
|
||||
# Copyright (c) 2014 OpenStack Foundation.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from ryu.base.app_manager import AppManager
|
||||
from ryu.controller.ofp_handler import OFPHandler
|
||||
|
||||
|
||||
from neutron import context
|
||||
|
||||
from dragonflow.controller.base_controller import ControllerBase
|
||||
from neutron.common import utils
|
||||
|
||||
|
||||
from neutron.openstack.common import log as logging
|
||||
|
||||
from dragonflow.controller.l3_openflow_app import L3ReactiveApp
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class OpenFlowController(ControllerBase):
|
||||
|
||||
def __init__(self, conf, controllertype):
|
||||
super(OpenFlowController, self).__init__(conf, controllertype)
|
||||
self.controllertype = controllertype
|
||||
self.ctx = context.get_admin_context()
|
||||
self.hostname = utils.get_hostname()
|
||||
self.sync_active_state = False
|
||||
self.sync_all = True
|
||||
self.l3_app = None
|
||||
self.heartbeat = None
|
||||
self.open_flow_hand = None
|
||||
self.start()
|
||||
|
||||
def start(self):
|
||||
app_mgr = AppManager.get_instance()
|
||||
LOG.debug(("running ryu openflow Controller lib "))
|
||||
self.open_flow_hand = app_mgr.instantiate(OFPHandler, None, None)
|
||||
self.open_flow_hand.start()
|
||||
self.l3_app = app_mgr.instantiate(L3ReactiveApp, None, None)
|
||||
self.l3_app.start()
|
||||
''' TODO fix this is hack to let the scheduler schedule the virtual
|
||||
router to L3 SDN app so this app will be in teh Agnet table as active
|
||||
Will be change when we convert this implementation to Service
|
||||
Plugin ----> l3 SDN agent for scalability Currently runs as tread
|
||||
will be converted to run as a standalone agent
|
||||
'''
|
||||
|
||||
def sync_router(self, router):
|
||||
self.l3_app.sync_router(router)
|
||||
|
||||
def sync_port(self, port):
|
||||
self.l3_app.sync_port(port)
|
|
@ -0,0 +1,360 @@
|
|||
# Copyright 2012 VMware, Inc. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
import eventlet
|
||||
from oslo_config import cfg
|
||||
import oslo_messaging
|
||||
from oslo_utils import excutils
|
||||
from oslo_utils import timeutils
|
||||
|
||||
from dragonflow.controller import openflow_controller as of_controller
|
||||
#from neutron.agent.common import config
|
||||
from neutron.agent.l3 import agent
|
||||
from neutron.agent.l3 import router_processing_queue as queue
|
||||
from neutron.agent import rpc as agent_rpc
|
||||
from neutron.common import constants as l3_constants
|
||||
from neutron.common import topics
|
||||
from neutron.common import utils as common_utils
|
||||
from neutron import context as n_context
|
||||
from neutron.i18n import _LE, _LI, _LW
|
||||
from neutron import manager
|
||||
from neutron.openstack.common import log as logging
|
||||
from neutron.openstack.common import loopingcall
|
||||
from neutron.openstack.common import periodic_task
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
NET_CONTROL_L3_OPTS = [
|
||||
cfg.StrOpt('L3controller_ip_list',
|
||||
default='tcp:172.16.10.10:6633',
|
||||
help=("L3 Controler IP list list tcp:ip_addr:port;"
|
||||
"tcp:ip_addr:port..;..")),
|
||||
cfg.StrOpt('net_controller_l3_southbound_protocol',
|
||||
default='OpenFlow',
|
||||
help=("Southbound protocol to connect the forwarding"
|
||||
"element Currently supports only OpenFlow"))
|
||||
]
|
||||
|
||||
|
||||
cfg.CONF.register_opts(NET_CONTROL_L3_OPTS)
|
||||
|
||||
|
||||
class L3ControllerAgent(manager.Manager):
|
||||
"""Manager for L3ControllerAgent
|
||||
|
||||
API version history:
|
||||
1.0 initial Version
|
||||
1.1 changed the type of the routers parameter
|
||||
to the routers_updated method.
|
||||
It was previously a list of routers in dict format.
|
||||
It is now a list of router IDs only.
|
||||
Per rpc versioning rules, it is backwards compatible.
|
||||
1.2 - DVR support: new L3 agent methods added.
|
||||
- add_arp_entry
|
||||
- del_arp_entry
|
||||
Needed by the L3 service when dealing with DVR
|
||||
"""
|
||||
target = oslo_messaging.Target(version='1.2')
|
||||
|
||||
def __init__(self, host, conf=None):
|
||||
if conf:
|
||||
self.conf = conf
|
||||
else:
|
||||
self.conf = cfg.CONF
|
||||
self.router_info = {}
|
||||
|
||||
self._check_config_params()
|
||||
self.context = n_context.get_admin_context_without_session()
|
||||
self.plugin_rpc = agent.L3PluginApi(topics.L3PLUGIN, host)
|
||||
self.fullsync = True
|
||||
|
||||
# Get the list of service plugins from Neutron Server
|
||||
# This is the first place where we contact neutron-server on startup
|
||||
# so retry in case its not ready to respond.
|
||||
retry_count = 5
|
||||
while True:
|
||||
retry_count = retry_count - 1
|
||||
try:
|
||||
self.neutron_service_plugins = (
|
||||
self.plugin_rpc.get_service_plugin_list(self.context))
|
||||
except oslo_messaging.RemoteError as e:
|
||||
with excutils.save_and_reraise_exception() as ctx:
|
||||
ctx.reraise = False
|
||||
LOG.warning(_LW('l3-agent cannot check service plugins '
|
||||
'enabled at the neutron server when '
|
||||
'startup due to RPC error. It happens '
|
||||
'when the server does not support this '
|
||||
'RPC API. If the error is '
|
||||
'UnsupportedVersion you can ignore this '
|
||||
'warning. Detail message: %s'), e)
|
||||
self.neutron_service_plugins = None
|
||||
except oslo_messaging.MessagingTimeout as e:
|
||||
with excutils.save_and_reraise_exception() as ctx:
|
||||
if retry_count > 0:
|
||||
ctx.reraise = False
|
||||
LOG.warning(_LW('l3-agent cannot check service '
|
||||
'plugins enabled on the neutron '
|
||||
'server. Retrying. '
|
||||
'Detail message: %s'), e)
|
||||
continue
|
||||
break
|
||||
|
||||
if cfg.CONF.net_controller_l3_southbound_protocol == "OpenFlow":
|
||||
# Open Flow Controller
|
||||
LOG.info(("Using Southbound OpenFlow Protocol "))
|
||||
self.controller = of_controller.OpenFlowController(cfg, "openflow")
|
||||
|
||||
elif cfg.CONF.net_controller_l3_southbound_protocol == "OVSDB":
|
||||
LOG.error(("Southbound OVSDB Protocol not implemented yet"))
|
||||
elif cfg.CONF.net_controller_l3_southbound_protocol == "OP-FLEX":
|
||||
LOG.error(("Southbound OP-FLEX Protocol not implemented yet"))
|
||||
self._queue = queue.RouterProcessingQueue()
|
||||
#self.event_observers = event_observers.L3EventObservers()
|
||||
super(L3ControllerAgent, self).__init__()
|
||||
|
||||
def _check_config_params(self):
|
||||
"""Check items in configuration files.
|
||||
|
||||
Check for required and invalid configuration items.
|
||||
The actual values are not verified for correctness.
|
||||
"""
|
||||
|
||||
@common_utils.exception_logger()
|
||||
def process_router(self, ri):
|
||||
# TODO(mrsmith) - we shouldn't need to check here
|
||||
if 'distributed' not in ri.router:
|
||||
ri.router['distributed'] = False
|
||||
ex_gw_port = self._get_ex_gw_port(ri)
|
||||
if ri.router.get('distributed') and ex_gw_port:
|
||||
ri.fip_ns = self.get_fip_ns(ex_gw_port['network_id'])
|
||||
ri.fip_ns.scan_fip_ports(ri)
|
||||
self._process_internal_ports(ri)
|
||||
self._process_external(ri)
|
||||
# Process static routes for router
|
||||
ri.routes_updated()
|
||||
|
||||
# Enable or disable keepalived for ha routers
|
||||
self._process_ha_router(ri)
|
||||
|
||||
# Update ex_gw_port and enable_snat on the router info cache
|
||||
ri.ex_gw_port = ex_gw_port
|
||||
ri.snat_ports = ri.router.get(l3_constants.SNAT_ROUTER_INTF_KEY, [])
|
||||
ri.enable_snat = ri.router.get('enable_snat')
|
||||
|
||||
def router_deleted(self, context, router_id):
|
||||
"""Deal with router deletion RPC message."""
|
||||
LOG.debug('Got router deleted notification for %s', router_id)
|
||||
update = queue.RouterUpdate(router_id,
|
||||
queue.PRIORITY_RPC,
|
||||
action=queue.DELETE_ROUTER)
|
||||
self._queue.add(update)
|
||||
|
||||
def routers_updated(self, context, routers):
|
||||
"""Deal with routers modification and creation RPC message."""
|
||||
LOG.debug('Got routers updated notification :%s', routers)
|
||||
if routers:
|
||||
# This is needed for backward compatibility
|
||||
if isinstance(routers[0], dict):
|
||||
routers = [router['id'] for router in routers]
|
||||
for id in routers:
|
||||
update = queue.RouterUpdate(id, queue.PRIORITY_RPC)
|
||||
self._queue.add(update)
|
||||
|
||||
def router_removed_from_agent(self, context, payload):
|
||||
LOG.debug('Got router removed from agent :%r', payload)
|
||||
router_id = payload['router_id']
|
||||
update = queue.RouterUpdate(router_id,
|
||||
queue.PRIORITY_RPC,
|
||||
action=queue.DELETE_ROUTER)
|
||||
self._queue.add(update)
|
||||
|
||||
def router_added_to_agent(self, context, payload):
|
||||
LOG.debug('Got router added to agent :%r', payload)
|
||||
self.routers_updated(context, payload)
|
||||
|
||||
def _process_router_update(self):
|
||||
for rp, update in self._queue.each_update_to_next_router():
|
||||
LOG.debug("Starting router update for %s", update.id)
|
||||
router = update.router
|
||||
if update.action != queue.DELETE_ROUTER and not router:
|
||||
try:
|
||||
update.timestamp = timeutils.utcnow()
|
||||
routers = self.plugin_rpc.get_routers(self.context,
|
||||
[update.id])
|
||||
except Exception:
|
||||
msg = _LE("Failed to fetch router information for '%s'")
|
||||
LOG.exception(msg, update.id)
|
||||
self.fullsync = True
|
||||
continue
|
||||
|
||||
if routers:
|
||||
router = routers[0]
|
||||
|
||||
if not router:
|
||||
self._router_removed(update.id)
|
||||
continue
|
||||
|
||||
#self._process_router_if_compatible(router)
|
||||
self.controller.sync_router(router)
|
||||
for interface in router['_interfaces']:
|
||||
self.sync_subnet_port_data(interface['subnet']['id'])
|
||||
LOG.debug("Finished a router update for %s", update.id)
|
||||
rp.fetched_and_processed(update.timestamp)
|
||||
|
||||
def _process_routers_loop(self):
|
||||
LOG.debug("Starting _process_routers_loop")
|
||||
pool = eventlet.GreenPool(size=8)
|
||||
while True:
|
||||
pool.spawn_n(self._process_router_update)
|
||||
|
||||
def sync_subnet_port_data(self, subnet_id):
|
||||
ports_data = self.plugin_rpc.get_ports_by_subnet(self.context,
|
||||
subnet_id)
|
||||
if ports_data:
|
||||
for port in ports_data:
|
||||
self.controller.sync_port(port)
|
||||
|
||||
@periodic_task.periodic_task
|
||||
def periodic_sync_routers_task(self, context):
|
||||
#if self.services_sync:
|
||||
LOG.debug("Starting periodic_sync_routers_task - fullsync:%s",
|
||||
self.fullsync)
|
||||
if not self.fullsync:
|
||||
return
|
||||
# self.fullsync is True at this point. If an exception -- caught or
|
||||
# uncaught -- prevents setting it to False below then the next call
|
||||
# to periodic_sync_routers_task will re-enter this code and try again.
|
||||
|
||||
# Capture a picture of namespaces *before* fetching the full list from
|
||||
# the database. This is important to correctly identify stale ones.
|
||||
prev_router_ids = set(self.router_info)
|
||||
timestamp = timeutils.utcnow()
|
||||
|
||||
try:
|
||||
routers = self.plugin_rpc.get_routers(context)
|
||||
|
||||
except oslo_messaging.MessagingException:
|
||||
LOG.exception(_LE("Failed synchronizing routers due to RPC error"))
|
||||
else:
|
||||
LOG.debug('Processing :%r', routers)
|
||||
for r in routers:
|
||||
update = queue.RouterUpdate(r['id'],
|
||||
queue.PRIORITY_SYNC_ROUTERS_TASK,
|
||||
router=r,
|
||||
timestamp=timestamp)
|
||||
self._queue.add(update)
|
||||
#if self.fullsync:
|
||||
|
||||
self.fullsync = False
|
||||
LOG.debug("periodic_sync_routers_task successfully completed")
|
||||
|
||||
curr_router_ids = set([r['id'] for r in routers])
|
||||
|
||||
# Two kinds of stale routers: Routers for which info is cached in
|
||||
# self.router_info and the others. First, handle the former.
|
||||
for router_id in prev_router_ids - curr_router_ids:
|
||||
update = queue.RouterUpdate(router_id,
|
||||
queue.PRIORITY_SYNC_ROUTERS_TASK,
|
||||
timestamp=timestamp,
|
||||
action=queue.DELETE_ROUTER)
|
||||
self._queue.add(update)
|
||||
|
||||
def after_start(self):
|
||||
eventlet.spawn_n(self._process_routers_loop)
|
||||
LOG.info(_LI("L3 agent started"))
|
||||
# When L3 agent is ready, we immediately do a full sync
|
||||
self.periodic_sync_routers_task(self.context)
|
||||
|
||||
def add_arp_entry(self, context, payload):
|
||||
"""Add arp entry into router namespace. Called from RPC."""
|
||||
port = payload['arp_table']
|
||||
self.controller.sync_port(port)
|
||||
|
||||
def del_arp_entry(self, context, payload):
|
||||
"""Delete arp entry from router namespace. Called from RPC."""
|
||||
#arp_table = payload['arp_table']
|
||||
# TODO(gampel) FIX add call to controller to delte entry
|
||||
LOG.debug("NOT IMP YET del_arp_entry")
|
||||
|
||||
|
||||
class L3ControllerAgentWithStateReport(L3ControllerAgent):
|
||||
|
||||
def __init__(self, host, conf=None):
|
||||
super(L3ControllerAgentWithStateReport, self).__init__(host=host,
|
||||
conf=conf)
|
||||
self.state_rpc = agent_rpc.PluginReportStateAPI(topics.PLUGIN)
|
||||
self.agent_state = {
|
||||
'binary': 'neutron-l3-controller-agent',
|
||||
'host': host,
|
||||
'topic': topics.L3_AGENT,
|
||||
'configurations': {
|
||||
#'agent_mode': self.conf.agent_mode,
|
||||
'agent_mode': "sdn",
|
||||
'router_id': self.conf.router_id,
|
||||
'handle_internal_only_routers':
|
||||
self.conf.handle_internal_only_routers,
|
||||
'external_network_bridge': self.conf.external_network_bridge,
|
||||
'gateway_external_network_id':
|
||||
self.conf.gateway_external_network_id},
|
||||
'start_flag': True,
|
||||
'agent_type': l3_constants.AGENT_TYPE_L3}
|
||||
report_interval = self.conf.AGENT.report_interval
|
||||
self.use_call = True
|
||||
if report_interval:
|
||||
self.heartbeat = loopingcall.FixedIntervalLoopingCall(
|
||||
self._report_state)
|
||||
self.heartbeat.start(interval=report_interval)
|
||||
|
||||
def _report_state(self):
|
||||
LOG.debug("Report state task started")
|
||||
num_ex_gw_ports = 0
|
||||
num_interfaces = 0
|
||||
num_floating_ips = 0
|
||||
num_routers = 2
|
||||
# router_infos = self.router_info.values()
|
||||
# num_routers = len(router_infos)
|
||||
# for ri in router_infos:
|
||||
# ex_gw_port = self._get_ex_gw_port(ri)
|
||||
# if ex_gw_port:
|
||||
# num_ex_gw_ports += 1
|
||||
# num_interfaces += len(ri.router.get(l3_constants.INTERFACE_KEY,
|
||||
# []))
|
||||
# num_floating_ips += len(ri.router.get(l3_constants.FLOATINGIP_KEY,
|
||||
# []))
|
||||
configurations = self.agent_state['configurations']
|
||||
configurations['routers'] = num_routers
|
||||
configurations['ex_gw_ports'] = num_ex_gw_ports
|
||||
configurations['interfaces'] = num_interfaces
|
||||
configurations['floating_ips'] = num_floating_ips
|
||||
try:
|
||||
self.state_rpc.report_state(self.context, self.agent_state,
|
||||
self.use_call)
|
||||
self.agent_state.pop('start_flag', None)
|
||||
self.use_call = False
|
||||
LOG.debug("Report state task successfully completed")
|
||||
except AttributeError:
|
||||
# This means the server does not support report_state
|
||||
LOG.warn(_LW("Neutron server does not support state report."
|
||||
" State report for this agent will be disabled."))
|
||||
self.heartbeat.stop()
|
||||
return
|
||||
except Exception:
|
||||
LOG.exception(_LE("Failed reporting state!"))
|
||||
|
||||
def agent_updated(self, context, payload):
|
||||
"""Handle the agent_updated notification event."""
|
||||
self.fullsync = True
|
||||
LOG.info(_LI("agent_updated by server side %s!"), payload)
|
|
@ -0,0 +1,245 @@
|
|||
# Copyright (c) 2014 OpenStack Foundation.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from oslo.config import cfg
|
||||
from oslo import messaging
|
||||
from oslo.utils import importutils
|
||||
|
||||
from neutron import context
|
||||
from neutron import manager
|
||||
|
||||
from neutron.api.rpc.agentnotifiers import l3_rpc_agent_api
|
||||
from neutron.api.rpc.handlers import l3_rpc
|
||||
from neutron.common import constants as q_const
|
||||
from neutron.common import rpc as n_rpc
|
||||
from neutron.common import topics
|
||||
from neutron.plugins.common import constants
|
||||
from neutron.plugins.ml2 import driver_api as api
|
||||
|
||||
from neutron.db import common_db_mixin
|
||||
from neutron.db import l3_gwmode_db
|
||||
from neutron.db import l3_hascheduler_db
|
||||
|
||||
from neutron.openstack.common import log as logging
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
NET_CONTROL_L3_OPTS = [
|
||||
cfg.StrOpt('L3controller_ip_list',
|
||||
default='tcp:172.16.10.10:6633',
|
||||
help=("L3 Controler IP list list tcp:ip_addr:port;"
|
||||
"tcp:ip_addr:port..;..")),
|
||||
cfg.StrOpt('net_controller_l3_southbound_protocol',
|
||||
default='OpenFlow',
|
||||
help=("Southbound protocol to connect the forwarding"
|
||||
"element Currently supports only OpenFlow"))
|
||||
]
|
||||
|
||||
cfg.CONF.register_opts(NET_CONTROL_L3_OPTS)
|
||||
|
||||
|
||||
class ControllerL3ServicePlugin(common_db_mixin.CommonDbMixin,
|
||||
l3_gwmode_db.L3_NAT_db_mixin,
|
||||
l3_hascheduler_db.L3_HA_scheduler_db_mixin,
|
||||
l3_rpc.L3RpcCallback):
|
||||
|
||||
RPC_API_VERSION = '1.2'
|
||||
supported_extension_aliases = ["router", "ext-gw-mode", "dvr",
|
||||
"l3_agent_scheduler"]
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self.setup_rpc()
|
||||
self.router_scheduler = importutils.import_object(
|
||||
cfg.CONF.router_scheduler_driver)
|
||||
#self.start_periodic_agent_status_check()
|
||||
self.ctx = context.get_admin_context()
|
||||
cfg.CONF.router_auto_schedule = True
|
||||
if cfg.CONF.net_controller_l3_southbound_protocol == "OpenFlow":
|
||||
# Open Flow Controller
|
||||
LOG.info(("Using Southbound OpenFlow Protocol "))
|
||||
|
||||
self.send_set_controllers_update(self.ctx, True)
|
||||
|
||||
#self.controllerThread = ControllerRunner("openflow")
|
||||
#self.controllerThread.start()
|
||||
#self.controllerThread.router_scheduler = self.router_scheduler
|
||||
#self.controllerThread.endpoints = self.endpoints
|
||||
|
||||
elif cfg.CONF.net_controller_l3_southbound_protocol == "OVSDB":
|
||||
LOG.error(("Southbound OVSDB Protocol not implemented yet"))
|
||||
elif cfg.CONF.net_controller_l3_southbound_protocol == "OP-FLEX":
|
||||
LOG.error(("Southbound OP-FLEX Protocol not implemented yet"))
|
||||
|
||||
super(ControllerL3ServicePlugin, self).__init__()
|
||||
|
||||
def setup_rpc(self):
|
||||
# RPC support
|
||||
self.topic = topics.L3PLUGIN
|
||||
self.conn = n_rpc.create_connection(new=True)
|
||||
self.agent_notifiers.update(
|
||||
{q_const.AGENT_TYPE_L3: l3_rpc_agent_api.L3AgentNotifyAPI()})
|
||||
self.endpoints = [self]
|
||||
self.conn.create_consumer(self.topic, self.endpoints,
|
||||
fanout=True)
|
||||
self.conn.consume_in_threads()
|
||||
|
||||
def get_plugin_type(self):
|
||||
return constants.L3_ROUTER_NAT
|
||||
|
||||
def get_plugin_description(self):
|
||||
"""Returns string description of the plugin."""
|
||||
return "L3 SDN Controller For Neutron"
|
||||
|
||||
def dvr_vmarp_table_update(self, context, port_dict, action):
|
||||
"""Notify the L3 agent of VM ARP table changes.
|
||||
|
||||
Provide the details of the VM ARP to the L3 agent when
|
||||
a Nova instance gets created or deleted.
|
||||
"""
|
||||
# Check this is a valid VM port
|
||||
if ("compute:" not in port_dict['device_owner'] or
|
||||
not port_dict['fixed_ips']):
|
||||
return
|
||||
#ip_address = port_dict['fixed_ips'][0]['ip_address']
|
||||
subnet = port_dict['fixed_ips'][0]['subnet_id']
|
||||
filters = {'fixed_ips': {'subnet_id': [subnet]}}
|
||||
ports = self._core_plugin.get_ports(context, filters=filters)
|
||||
for port in ports:
|
||||
if (port['device_owner'] == q_const.DEVICE_OWNER_ROUTER_INTF or
|
||||
port['device_owner'] == q_const.DEVICE_OWNER_DVR_INTERFACE):
|
||||
router_id = port['device_id']
|
||||
#router_dict = self._get_router(context, router_id)
|
||||
port_data = self.get_ml2_port_bond_data(context, port['id'],
|
||||
port['binding:host_id'])
|
||||
segmentation_id = 0
|
||||
if "segmentation_id" in port_data:
|
||||
segmentation_id = port_data['segmentation_id']
|
||||
port['segmentation_id'] = segmentation_id
|
||||
if action == "add":
|
||||
notify_action = self.l3_rpc_notifier.add_arp_entry
|
||||
elif action == "del":
|
||||
notify_action = self.l3_rpc_notifier.del_arp_entry
|
||||
notify_action(context, router_id, port)
|
||||
self.send_set_controllers_update(context, False)
|
||||
return
|
||||
|
||||
def get_ports_by_subnet(self, context, **kwargs):
|
||||
result = super(ControllerL3ServicePlugin, self).get_ports_by_subnet(
|
||||
context,
|
||||
**kwargs)
|
||||
if result:
|
||||
for port in result:
|
||||
port_data = self.get_ml2_port_bond_data(context, port['id'],
|
||||
port['binding:host_id'])
|
||||
|
||||
segmentation_id = 0
|
||||
if "segmentation_id" in port_data:
|
||||
segmentation_id = port_data['segmentation_id']
|
||||
port['segmentation_id'] = segmentation_id
|
||||
return result
|
||||
|
||||
def get_ml2_port_bond_data(self, ctx, port_id, device_id):
|
||||
core_plugin = manager.NeutronManager.get_plugin()
|
||||
port_context = core_plugin.get_bound_port_context(
|
||||
ctx, port_id, device_id)
|
||||
if not port_context:
|
||||
LOG.warning(("Device %(device)s requested by agent "
|
||||
"%(agent_id)s not found in database"),
|
||||
{'device': device_id, 'agent_id': port_id})
|
||||
return {None}
|
||||
|
||||
segment = port_context.bottom_bound_segment
|
||||
port = port_context.current
|
||||
|
||||
if not segment:
|
||||
LOG.warning(("Device %(device)s requested by agent "
|
||||
" on network %(network_id)s not "
|
||||
"bound, vif_type: "),
|
||||
{'device': device_id,
|
||||
'network_id': port['network_id']})
|
||||
return {None}
|
||||
|
||||
entry = {'device': device_id,
|
||||
'network_id': port['network_id'],
|
||||
'port_id': port_id,
|
||||
'mac_address': port['mac_address'],
|
||||
'admin_state_up': port['admin_state_up'],
|
||||
'network_type': segment[api.NETWORK_TYPE],
|
||||
'segmentation_id': segment[api.SEGMENTATION_ID],
|
||||
'physical_network': segment[api.PHYSICAL_NETWORK],
|
||||
'fixed_ips': port['fixed_ips'],
|
||||
'device_owner': port['device_owner']}
|
||||
LOG.debug(("Returning: %s"), entry)
|
||||
return entry
|
||||
|
||||
def auto_schedule_routers(self, context, host, router_ids):
|
||||
l3_agent = self.get_enabled_agent_on_host(
|
||||
context, q_const.AGENT_TYPE_L3, host)
|
||||
if not l3_agent:
|
||||
return False
|
||||
if self.router_scheduler:
|
||||
unscheduled_rs = self.router_scheduler.get_routers_to_schedule(
|
||||
context,
|
||||
self,
|
||||
router_ids)
|
||||
|
||||
self.router_scheduler.bind_routers(context, self,
|
||||
unscheduled_rs,
|
||||
l3_agent)
|
||||
return
|
||||
|
||||
def setup_vrouter_arp_responder(self, _context, br, action, table_id,
|
||||
segmentation_id, net_uuid, mac_address,
|
||||
ip_address):
|
||||
|
||||
topic_port_update = topics.get_topic_name(topics.AGENT,
|
||||
topics.PORT,
|
||||
topics.UPDATE)
|
||||
target = messaging.Target(topic=topic_port_update)
|
||||
rpcapi = n_rpc.get_client(target)
|
||||
rpcapi.cast(_context,
|
||||
'setup_entry_for_arp_reply_remote',
|
||||
br_id="br-int",
|
||||
action=action,
|
||||
table_id=table_id,
|
||||
segmentation_id=segmentation_id,
|
||||
net_uuid=net_uuid,
|
||||
mac_address=mac_address,
|
||||
ip_address=ip_address)
|
||||
|
||||
def update_agent_port_mapping_done(self, _context, agent_id, ip_address,
|
||||
host=None):
|
||||
LOG.debug(("::agent agent <%s> on ip <%s> host <%s> "),
|
||||
agent_id,
|
||||
ip_address,
|
||||
host)
|
||||
self.send_set_controllers_update(_context, False)
|
||||
|
||||
def send_set_controllers_update(self, _context, force_reconnect):
|
||||
|
||||
topic_port_update = topics.get_topic_name(topics.AGENT,
|
||||
topics.PORT,
|
||||
topics.UPDATE)
|
||||
target = messaging.Target(topic=topic_port_update)
|
||||
rpcapi = n_rpc.get_client(target)
|
||||
iplist = cfg.CONF.L3controller_ip_list
|
||||
|
||||
rpcapi.cast(_context,
|
||||
'set_controller_for_br',
|
||||
br_id="br-int",
|
||||
ip_address_list=iplist,
|
||||
force_reconnect=force_reconnect,
|
||||
protocols="OpenFlow13")
|
|
@ -0,0 +1,10 @@
|
|||
#!/usr/bin/python
|
||||
# EASY-INSTALL-ENTRY-SCRIPT: 'neutron==2015.1.dev109','console_scripts','neutron-l3-agent'
|
||||
__requires__ = 'dragonflow==0.1'
|
||||
import sys
|
||||
from pkg_resources import load_entry_point
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(
|
||||
load_entry_point('dragonflow==0.1', 'console_scripts', 'neutron-l3-controller-agent')()
|
||||
)
|
|
@ -1,5 +1,6 @@
|
|||
[metadata]
|
||||
name = dragonflow
|
||||
version = 0.1
|
||||
summary = SDN based Virtual Router add-on for Neutron OpenStack
|
||||
description-file =
|
||||
README.rst
|
||||
|
@ -41,3 +42,8 @@ input_file = dragonflow/locale/dragonflow.pot
|
|||
keywords = _ gettext ngettext l_ lazy_gettext
|
||||
mapping_file = babel.cfg
|
||||
output_file = dragonflow/locale/dragonflow.pot
|
||||
|
||||
|
||||
[entry_points]
|
||||
console_scripts =
|
||||
neutron-l3-controller-agent = dragonflow.neutron.agent.l3_sdn_agent:main
|
||||
|
|
30
tox.ini
30
tox.ini
|
@ -1,6 +1,6 @@
|
|||
[tox]
|
||||
minversion = 1.6
|
||||
envlist = py27,py33,py34,pep8
|
||||
envlist = py27,py33,pep8
|
||||
skipsdist = True
|
||||
|
||||
[testenv]
|
||||
|
@ -25,10 +25,26 @@ commands = python setup.py testr --coverage --testr-args='{posargs}'
|
|||
commands = python setup.py build_sphinx
|
||||
|
||||
[flake8]
|
||||
# H803 skipped on purpose per list discussion.
|
||||
# E123, E125 skipped as they are invalid PEP-8.
|
||||
|
||||
show-source = True
|
||||
ignore = E123,E125,H803
|
||||
# E125 continuation line does not distinguish itself from next logical line
|
||||
# E126 continuation line over-indented for hanging indent
|
||||
# E128 continuation line under-indented for visual indent
|
||||
# E129 visually indented line with same indent as next logical line
|
||||
# E265 block comment should start with ‘# ‘
|
||||
# H305 imports not grouped correctly
|
||||
# H307 like imports should be grouped together
|
||||
# H402 one line docstring needs punctuation
|
||||
# H404 multi line docstring should start with a summary
|
||||
# H405 multi line docstring summary not separated with an empty line
|
||||
# H904 Wrap long lines in parentheses instead of a backslash
|
||||
ignore = E125,E126,E128,E129,E265,H301,H305,H307,H402,H404,H405,H904
|
||||
show-source = true
|
||||
builtins = _
|
||||
exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build
|
||||
# TODO(dougw) neutron/tests/unit/vmware exclusion is a temporary services split hack
|
||||
exclude = .venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build,tools,.ropeproject,rally-scenarios,neutron/tests/unit/vmware*
|
||||
|
||||
[testenv:pylint]
|
||||
deps =
|
||||
{[testenv]deps}
|
||||
pylint
|
||||
commands =
|
||||
pylint --rcfile=.pylintrc --output-format=colorized {posargs:neutron}
|
||||
|
|
Loading…
Reference in New Issue