Framework for Neutron PowerVM Agent

Providing the baseline structure of the PowerVM Neutron Agent file.
This supports a generic call back to the controller node to state that
it is alive.  Should receive incoming port requests, but does not do
anything with those requests yet.

Initial Unit Test code is also provided.

Change-Id: I19455dab32aec23ef11ab3bccdecbff0630781e5
changes/89/152989/1
Drew Thorstensen 2014-10-22 14:25:52 -05:00
parent 9b2e01f87d
commit 3c4193fbc0
16 changed files with 253 additions and 27 deletions

View File

@ -1,4 +1,4 @@
[DEFAULT]
test_command=OS_STDOUT_CAPTURE=1 OS_STDERR_CAPTURE=1 OS_LOG_CAPTURE=1 ${PYTHON:-python} -m subunit.run discover -t ./ ${OS_TEST_PATH:-./neutron-powervm/tests/unit} $LISTOPT $IDOPTION
test_command=OS_STDOUT_CAPTURE=1 OS_STDERR_CAPTURE=1 OS_LOG_CAPTURE=1 ${PYTHON:-python} -m subunit.run discover -t ./ ${OS_TEST_PATH:-./neutron_powervm/tests/unit} $LISTOPT $IDOPTION
test_id_option=--load-list $IDFILE
test_list_option=--list

View File

@ -1,17 +0,0 @@
# Copyright 2014 IBM Corp.
#
# 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: Drew Thorstensen, IBM Corp.

View File

@ -0,0 +1,151 @@
# Copyright 2014 IBM Corp.
#
# 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: Drew Thorstensen, IBM Corp.
from oslo.config import cfg
from neutron.agent import rpc as agent_rpc
from neutron.common import constants as q_const
from neutron.common import rpc as n_rpc
from neutron.common import topics
from neutron import context as ctx
from neutron.openstack.common import log as logging
from neutron.openstack.common import loopingcall
LOG = logging.getLogger(__name__)
class SharedEthernetPluginApi(agent_rpc.PluginApi):
pass
class SharedEthernetRpcCallbacks(n_rpc.RpcCallback):
'''
Provides call backs (as defined in the setup_rpc method within the
SharedEthernetNeutronAgent class) that will be invoked upon certain
actions from the controller.
'''
# This agent supports RPC Version 1.0. For reference:
# 1.0 Initial version
# 1.1 Support Security Group RPC
# 1.2 Support DVR (Distributed Virtual Router) RPC
RPC_API_VERSION = '1.0'
def __init__(self, agent):
'''
Creates the call back. Most of the call back methods will be
delegated to the agent.
:param agent: The owning agent to delegate the callbacks to.
'''
super(SharedEthernetRpcCallbacks, self).__init__()
self.agent = agent
def port_update(self, context, **kwargs):
port_id = kwargs['port']['id']
# TODO Need to perform the call back
LOG.debug(_("port_update RPC received for port: %s"), port_id)
def network_delete(self, context, **kwargs):
network_id = kwargs.get('network_id')
# TODO Need to perform the call back
LOG.debug(_("network_delete RPC received for network: %s"), network_id)
class SharedEthernetNeutronAgent():
'''
Provides VLAN networks for the PowerVM platform that run accross the
Shared Ethernet within the Virtual I/O Servers. Designed to be compatible
with the ML2 Neutron Plugin.
'''
def __init__(self):
'''
Constructs the agent.
'''
# Define the baseline agent_state that will be reported back for the
# health status
self.agent_state = {
'binary': 'neutron-powervm-sharedethernet-agent',
'host': cfg.CONF.host,
'topic': q_const.L2_AGENT_TOPIC,
'configurations': {},
'agent_type': 'PowerVM Shared Ethernet agent',
'start_flag': True}
def setup_rpc(self):
'''
Registers the RPC consumers for the plugin.
'''
self.agent_id = 'sea-agent-%s' % cfg.CONF.host
self.topic = topics.AGENT
self.plugin_rpc = SharedEthernetPluginApi(topics.PLUGIN)
self.state_rpc = agent_rpc.PluginReportStateAPI(topics.PLUGIN)
self.context = ctx.get_admin_context_without_session()
# Defines what will be listening for incoming events from the
# controller.
self.endpoints = [SharedEthernetRpcCallbacks(self)]
# Define the listening consumers for the agent
# TODO We may just want to change this to port/update,
# port/create, and port/delete. Then plug on those?
consumers = [[topics.PORT, topics.UPDATE],
[topics.NETWORK, topics.DELETE]]
self.connection = agent_rpc.create_consumers(self.endpoints,
self.topic,
consumers)
report_interval = cfg.CONF.AGENT.report_interval
if report_interval:
heartbeat = loopingcall.FixedIntervalLoopingCall(
self._report_state)
heartbeat.start(interval=report_interval)
def _report_state(self):
'''
Reports the state of the agent back to the controller. Controller
knows that if a response isn't provided in a certain period of time
then the agent is dead. This call simply tells the controller that
the agent is alive.
'''
# TODO provide some level of devices connected to this agent.
try:
device_count = 0
self.agent_state.get('configurations')['devices'] = device_count
self.state_rpc.report_state(self.context,
self.agent_state)
self.agent_state.pop('start_flag', None)
except Exception:
LOG.exception(_("Failed reporting state!"))
def main():
agent = SharedEthernetNeutronAgent()
LOG.info(_("Shared Ethernet Agent initialized and running"))
agent.rpc_loop()
if __name__ == "__main__":
main()

View File

@ -0,0 +1,92 @@
# Copyright 2014 IBM Corp.
#
# 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: Drew Thorstensen, IBM Corp.
from oslo.config import cfg
import mock
from neutron_powervm.plugins.ibm.agent.powervm import powervm_sea_agent
from neutron.common import constants as q_const
from neutron import context as ctx
from neutron.tests import base
class SimpleTest(base.BaseTestCase):
def setUp(self):
super(SimpleTest, self).setUp()
self.agent = powervm_sea_agent.SharedEthernetNeutronAgent()
def test_init(self):
'''
Verifies the integrity of the agent after being initialized.
'''
temp_agent = powervm_sea_agent.SharedEthernetNeutronAgent()
self.assertEqual('neutron-powervm-sharedethernet-agent',
temp_agent.agent_state.get('binary'))
self.assertEqual(q_const.L2_AGENT_TOPIC,
temp_agent.agent_state.get('topic'))
self.assertEqual(True, temp_agent.agent_state.get('start_flag'))
self.assertEqual('PowerVM Shared Ethernet agent',
temp_agent.agent_state.get('agent_type'))
def test_report_state(self):
'''
Validates that the report state functions properly.
'''
# Make sure we had a start flag before the first report
self.assertIsNotNone(self.agent.agent_state.get('start_flag'))
# Mock up the state_rpc
self.agent.state_rpc = mock.Mock()
self.agent.context = mock.Mock()
# run the code
self.agent._report_state()
# Devices are not set
configs = self.agent.agent_state.get('configurations')
self.assertEqual(0, configs['devices'])
# Make sure we flipped to None after the report. Also
# indicates that we hit the last part of the method and didn't
# fail.
self.assertIsNone(self.agent.agent_state.get('start_flag'))
@mock.patch('neutron.openstack.common.loopingcall.'
'FixedIntervalLoopingCall')
@mock.patch.object(ctx, 'get_admin_context_without_session',
return_value=mock.Mock())
def test_setup_rpc(self, admin_ctxi, mock_loopingcall):
'''
Validates that the setup_rpc method is properly invoked
'''
cfg.CONF.AGENT = mock.Mock()
cfg.CONF.AGENT.report_interval = 5
# Derives the instance that will be returned when a new loopingcall
# is made. Used for verification
instance = mock_loopingcall.return_value
# Run the method to completion
self.agent.setup_rpc()
# Make sure that the loopingcall had an interval of 5.
instance.start.assert_called_with(interval=5)

View File

@ -1,4 +1,4 @@
[DEFAULT]
# The base module to hold the copy of openstack.common
base=neutron-powervm
base=neutron_powervm

View File

@ -22,7 +22,7 @@ console_scripts =
neutron-powervm-sea-agent = neutron.plugins.ibm.agent.powervm.powervm_sea_agent:main
[files]
packages = neutron-powervm
packages = neutron_powervm
[build_sphinx]
all_files = 1
@ -30,15 +30,15 @@ source-dir = doc/source
build-dir = doc/build
[compile_catalog]
directory = neutron-powervm/locale
directory = neutron_powervm/locale
domain = neutron-powervm
[update_catalog]
domain = neutron-powervm
output_dir = neutron-powervm/locale
input_file = neutron-powervm/locale/neutron-powervm.pot
output_dir = neutron_powervm/locale
input_file = neutron_powervm/locale/neutron-powervm.pot
[extract_messages]
keywords = _ gettext ngettext l_ lazy_gettext
mapping_file = babel.cfg
output_file = neutron-powervm/locale/neutron-powervm.pot
output_file = neutron_powervm/locale/neutron-powervm.pot

View File

@ -23,12 +23,12 @@ commands =
setenv = VIRTUAL_ENV={envdir}
[testenv:functional]
setenv = OS_TEST_PATH=./neutron-powervm/tests/functional
setenv = OS_TEST_PATH=./neutron_powervm/tests/functional
commands =
python -m neutron.openstack.common.lockutils python setup.py testr --slowest --testr-args='{posargs}'
[testenv:dsvm-functional]
setenv = OS_TEST_PATH=./neutron-powervm/tests/functional
setenv = OS_TEST_PATH=./neutron_powervm/tests/functional
OS_SUDO_TESTING=1
OS_ROOTWRAP_CMD=sudo /usr/local/bin/neutron-rootwrap /etc/neutron/rootwrap.conf
OS_FAIL_ON_MISSING_DEPS=1
@ -75,7 +75,7 @@ commands = python setup.py build_sphinx
# H405 multi line docstring summary not separated with an empty line
# H904 Wrap long lines in parentheses instead of a backslash
# TODO(marun) H404 multi line docstring should start with a summary
ignore = E125,E126,E128,E129,E265,E713,F402,F811,F812,H104,H237,H305,H307,H401,H402,H404,H405,H904
ignore = E125,E126,E128,E129,E265,E713,F402,F811,F812,H104,H237,H305,H307,H401,H402,H404,H405,H904,H101
show-source = true
builtins = _
exclude = .venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build,tools,.ropeproject,rally-scenarios