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:
Eran Gampel 2015-03-01 18:15:42 +02:00
parent bda541f819
commit 2167a8bafe
14 changed files with 2084 additions and 7 deletions

View File

View File

@ -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

View File

@ -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)

View File

View File

View File

View File

@ -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)

View File

View File

@ -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")

10
neutron-l3-controller-agent Executable file
View File

@ -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')()
)

View File

@ -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
View File

@ -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}