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: I19455dab32aec23ef11ab3bccdecbff0630781e5changes/89/152989/1
parent
9b2e01f87d
commit
3c4193fbc0
|
@ -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
|
||||
|
|
|
@ -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.
|
|
@ -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()
|
|
@ -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)
|
|
@ -1,4 +1,4 @@
|
|||
[DEFAULT]
|
||||
|
||||
# The base module to hold the copy of openstack.common
|
||||
base=neutron-powervm
|
||||
base=neutron_powervm
|
||||
|
|
10
setup.cfg
10
setup.cfg
|
@ -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
|
||||
|
|
6
tox.ini
6
tox.ini
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue