Add neutron driver for binding

Add new neutron plugin which enables port bind actions for network
fabrics. This driver waits until all ports are in state "active"
and sets the binding host flag. Default vNIC type is set to
"baremetal" in order to benefit from the code already in place for
Ironic. It's also possible to switch to 'normal' which assumes an
neutron agent in place.

The feature can be tested using the docker driver.

DocImpact

Co-Authored-By: Daniel Gonzalez <daniel@gonzalez-nothnagel.de>
Partially-Implements: bp manila-hpb-support

Change-Id: I3156d7468d48f84f1b46885780a2426f9b99a387
This commit is contained in:
Marc Koderer 2016-05-09 12:05:07 +02:00
parent bb767e8ece
commit 78e10bc060
14 changed files with 745 additions and 68 deletions

View File

@ -75,6 +75,12 @@ function configure_default_backends {
iniset $MANILA_CONF $group_name service_instance_user $MANILA_SERVICE_INSTANCE_USER
iniset $MANILA_CONF $group_name driver_handles_share_servers True
if [ "$SHARE_DRIVER" == $MANILA_CONTAINER_DRIVER ]; then
iniset $MANILA_CONF $group_name network_api_class $MANILA_NETWORK_API_CLASS
iniset $MANILA_CONF $group_name neutron_host_id $(hostname)
iniset $MANILA_CONF $group_name neutron_vnic_type $MANILA_NEUTRON_VNIC_TYPE
fi
if [ $(trueorfalse False MANILA_USE_SERVICE_INSTANCE_PASSWORD) == True ]; then
iniset $MANILA_CONF $group_name service_instance_password $MANILA_SERVICE_INSTANCE_PASSWORD
fi
@ -522,7 +528,6 @@ function init_manila {
elif [ "$SHARE_DRIVER" == $MANILA_CONTAINER_DRIVER ]; then
if is_service_enabled m-shr; then
SHARE_GROUP=$MANILA_CONTAINER_VOLUME_GROUP_NAME
iniset $MANILA_CONF DEFAULT neutron_host_id $(hostname)
configure_backing_file
fi

View File

@ -166,6 +166,10 @@ MANILA_CONTAINER_VOLUME_GROUP_NAME=${MANILA_CONTAINER_VOLUME_GROUP_NAME:-"manila
# permanent one as soon as possible.
MANILA_DOCKER_IMAGE_URL=${MANILA_DOCKER_IMAGE_URL:-"https://github.com/a-ovchinnikov/manila-image-elements-lxd-images/releases/download/0.1.0/manila-docker-container.tar.gz"}
# Network Plugin
MANILA_NETWORK_API_CLASS=${MANILA_NETWORK_API_CLASS:-"manila.network.neutron.neutron_network_plugin.NeutronBindNetworkPlugin"}
MANILA_NEUTRON_VNIC_TYPE=${MANILA_NEUTRON_VNIC_TYPE:-"normal"}
# Enable manila services
# ----------------------
# We have to add Manila to enabled services for screen_it to work

View File

@ -77,6 +77,13 @@ There are three different network plugins and five python classes in Manila:
`neutron_net_id` and `neutron_subnet_id` from the Manila configuration
file and uses one network for all shares.
1.3 `manila.network.neutron.neutron_network_plugin.NeutronBindNetworkPlugin`.
This driver waits for active binding and fails if a Neutron port can't be
bound or an error occurs. This plugin is useful for agent based binding
(like OVS with docker driver) and fabric binding where real HW
reconfiguration is taking place. The existing
`NeutronBindSingleNetworkPlugin` is a combination of 1.2 and 1.3.
When only a single network is needed, the NeutronSingleNetworkPlugin (1.2)
is a simple solution. Otherwise NeutronNetworkPlugin (1.1) should be chosen.

View File

@ -114,6 +114,10 @@ class NetworkException(ManilaException):
message = _("Exception due to network failure.")
class NetworkBindException(ManilaException):
message = _("Exception due to failed port status in binding.")
class NetworkBadConfigurationException(NetworkException):
message = _("Bad network configuration: %(reason)s.")

View File

@ -65,6 +65,17 @@ class NetworkBaseAPI(db_base.Base):
"'%s'.") % share_server_id
raise exception.NetworkBadConfigurationException(reason=msg)
def update_network_allocation(self, context, share_server):
"""Update network allocation.
Optional method to be called by the manager after share server creation
which can be overloaded in case the port state has to be updated.
:param context: RequestContext object
:param share_server: share server object
:return: list of updated ports or None if nothing was updated
"""
@abc.abstractmethod
def allocate_network(self, context, share_server, share_network=None,
**kwargs):

View File

@ -164,7 +164,8 @@ class API(object):
def create_port(self, tenant_id, network_id, host_id=None, subnet_id=None,
fixed_ip=None, device_owner=None, device_id=None,
mac_address=None, security_group_ids=None, dhcp_opts=None):
mac_address=None, security_group_ids=None, dhcp_opts=None,
**kwargs):
try:
port_req_body = {'port': {}}
port_req_body['port']['network_id'] = network_id
@ -187,6 +188,8 @@ class API(object):
port_req_body['port']['device_owner'] = device_owner
if device_id:
port_req_body['port']['device_id'] = device_id
if kwargs:
port_req_body['port'].update(kwargs)
port = self.client.create_port(port_req_body).get('port', {})
return port
except neutron_client_exc.NeutronClientException as e:

View File

@ -15,3 +15,8 @@
PROVIDER_NW_EXT = 'Provider Network'
PORTBINDING_EXT = 'Port Binding'
PORT_STATUS_ERROR = 'ERROR'
PORT_STATUS_ACTIVE = 'ACTIVE'
VIF_TYPE_BINDING_FAILED = 'binding_failed'

View File

@ -14,15 +14,21 @@
# License for the specific language governing permissions and limitations
# under the License.
import socket
from oslo_config import cfg
from oslo_log import log
from manila.common import constants
from manila import exception
from manila.i18n import _
from manila import network
from manila.network.neutron import api as neutron_api
from manila.network.neutron import constants as neutron_constants
from manila import utils
LOG = log.getLogger(__name__)
neutron_single_network_plugin_opts = [
cfg.StrOpt(
'neutron_net_id',
@ -39,12 +45,18 @@ neutron_single_network_plugin_opts = [
deprecated_group='DEFAULT'),
]
neutron_network_plugin_opts = [
neutron_bind_network_plugin_opts = [
cfg.StrOpt(
'neutron_vnic_type',
help="vNIC type used for binding.",
choices=['baremetal', 'normal', 'direct',
'direct-physical', 'macvtap'],
default='baremetal'),
cfg.StrOpt(
"neutron_host_id",
help="Host ID to be used when creating neutron port. Hostname of "
"a controller running Neutron should be used in a general case.",
deprecated_group='DEFAULT'),
help="Host ID to be used when creating neutron port. If not set "
"host is set to manila-share host by default.",
default=socket.gethostname()),
]
CONF = cfg.CONF
@ -59,9 +71,6 @@ class NeutronNetworkPlugin(network.NetworkBaseAPI):
self._neutron_api_args = args
self._neutron_api_kwargs = kwargs
self._label = kwargs.pop('label', 'user')
CONF.register_opts(
neutron_network_plugin_opts,
group=self.neutron_api.config_group_name)
@property
def label(self):
@ -124,13 +133,22 @@ class NeutronNetworkPlugin(network.NetworkBaseAPI):
for port in ports:
self._delete_port(context, port)
def _get_port_create_args(self, share_server, share_network,
device_owner):
return {
"network_id": share_network['neutron_net_id'],
"subnet_id": share_network['neutron_subnet_id'],
"device_owner": 'manila:' + device_owner,
"device_id": share_server.get('id'),
}
def _create_port(self, context, share_server, share_network, device_owner):
host_id = self.neutron_api.configuration.neutron_host_id
create_args = self._get_port_create_args(share_server, share_network,
device_owner)
port = self.neutron_api.create_port(
share_network['project_id'],
network_id=share_network['neutron_net_id'],
subnet_id=share_network['neutron_subnet_id'],
device_owner='manila:' + device_owner, host_id=host_id)
share_network['project_id'], **create_args)
port_dict = {
'id': port['id'],
'share_server_id': share_server['id'],
@ -214,7 +232,7 @@ class NeutronSingleNetworkPlugin(NeutronNetworkPlugin):
'neutron_net_id': self.net,
'neutron_subnet_id': self.subnet,
}
super(NeutronSingleNetworkPlugin, self).allocate_network(
return super(NeutronSingleNetworkPlugin, self).allocate_network(
context, share_server, share_network, **kwargs)
def _verify_net_and_subnet(self):
@ -261,3 +279,73 @@ class NeutronSingleNetworkPlugin(NeutronNetworkPlugin):
share_network = self.db.share_network_update(
context, share_network['id'], upd)
return share_network
class NeutronBindNetworkPlugin(NeutronNetworkPlugin):
def __init__(self, *args, **kwargs):
super(NeutronBindNetworkPlugin, self).__init__(*args, **kwargs)
CONF.register_opts(
neutron_bind_network_plugin_opts,
group=self.neutron_api.config_group_name)
self.config = self.neutron_api.configuration
def update_network_allocation(self, context, share_server):
if self.config.neutron_vnic_type == 'normal':
ports = self.db.network_allocations_get_for_share_server(
context,
share_server['id'])
self._wait_for_ports_bind(ports, share_server)
return ports
@utils.retry(exception.NetworkBindException, retries=20)
def _wait_for_ports_bind(self, ports, share_server):
inactive_ports = []
for port in ports:
port = self._neutron_api.show_port(port['id'])
if (port['status'] == neutron_constants.PORT_STATUS_ERROR or
('binding:vif_type' in port and
port['binding:vif_type'] ==
neutron_constants.VIF_TYPE_BINDING_FAILED)):
msg = _("Port binding %s failed.") % port['id']
raise exception.NetworkException(msg)
elif port['status'] != neutron_constants.PORT_STATUS_ACTIVE:
LOG.debug("The port %(id)s is in state %(state)s. "
"Wait for active state.", {
"id": port['id'],
"state": port['status']})
inactive_ports.append(port['id'])
if len(inactive_ports) == 0:
return
msg = _("Ports are not fully bound for share server "
"'%(s_id)s' (inactive ports: %(ports)s)") % {
"s_id": share_server['id'],
"ports": inactive_ports}
raise exception.NetworkBindException(msg)
def _get_port_create_args(self, share_server, share_network,
device_owner):
arguments = super(
NeutronBindNetworkPlugin, self)._get_port_create_args(
share_network, share_network, device_owner)
arguments['host_id'] = self.config.neutron_host_id
arguments['binding:vnic_type'] = self.config.neutron_vnic_type
return arguments
def allocate_network(self, context, share_server, share_network=None,
**kwargs):
ports = super(NeutronBindNetworkPlugin, self).allocate_network(
context, share_server, share_network, **kwargs)
# If vnic type is 'normal' we expect a neutron agent to bind the
# ports. This action requires a vnic to be spawned by the driver.
# Therefore we do not wait for the port binding here, but
# return the unbound ports and expect the share manager to call
# update_network_allocation after the share server was created, in
# order to update the ports with the correct binding.
if self.config.neutron_vnic_type != 'normal':
self._wait_for_ports_bind(ports, share_server)
return ports
class NeutronBindSingleNetworkPlugin(NeutronSingleNetworkPlugin,
NeutronBindNetworkPlugin):
pass

View File

@ -100,7 +100,7 @@ _global_opt_lists = [
manila.network.linux.interface.OPTS,
manila.network.network_opts,
manila.network.neutron.neutron_network_plugin.
neutron_network_plugin_opts,
neutron_bind_network_plugin_opts,
manila.network.neutron.neutron_network_plugin.
neutron_single_network_plugin_opts,
manila.network.nova_network_plugin.nova_single_network_plugin_opts,

View File

@ -570,6 +570,17 @@ class ShareDriver(object):
def get_admin_network_allocations_number(self):
return 0
def update_network_allocation(self, context, share_server):
"""Update network allocation after share server creation."""
self.network_api.update_network_allocation(context, share_server)
def update_admin_network_allocation(self, context, share_server):
"""Update admin network allocation after share server creation."""
if (self.get_admin_network_allocations_number() and
self.admin_network_api):
self.admin_network_api.update_network_allocation(context,
share_server)
def allocate_network(self, context, share_server, share_network,
count=None, **kwargs):
"""Allocate network resources using given network information."""

View File

@ -2375,6 +2375,9 @@ class ShareManager(manager.SchedulerDependentManager):
server_info = self.driver.setup_server(
network_info, metadata=metadata)
self.driver.update_network_allocation(context, share_server)
self.driver.update_admin_network_allocation(context, share_server)
if server_info and isinstance(server_info, dict):
self.db.share_server_backend_details_set(
context, share_server['id'], server_info)

View File

@ -164,6 +164,25 @@ class NeutronApiTest(test.TestCase):
self.neutron_api._has_port_binding_extension.assert_called_once_with()
self.assertTrue(clientv20.Client.called)
def test_create_port_with_additional_kwargs(self):
# Set up test data
self.mock_object(self.neutron_api, '_has_port_binding_extension',
mock.Mock(return_value=True))
port_args = {'tenant_id': 'test tenant', 'network_id': 'test net',
'binding_arg': 'foo'}
# Execute method 'create_port'
port = self.neutron_api.create_port(**port_args)
# Verify results
self.assertEqual(port_args['tenant_id'], port['tenant_id'])
self.assertEqual(port_args['network_id'],
port['network_id'])
self.assertEqual(port_args['binding_arg'],
port['binding_arg'])
self.neutron_api._has_port_binding_extension.assert_called_once_with()
self.assertTrue(clientv20.Client.called)
@mock.patch.object(neutron_api.LOG, 'exception', mock.Mock())
def test_create_port_exception(self):
# Set up test data

View File

@ -14,8 +14,11 @@
# License for the specific language governing permissions and limitations
# under the License.
import copy
import ddt
import mock
import time
from oslo_config import cfg
from manila.common import constants
@ -31,7 +34,7 @@ from manila.tests import utils as test_utils
CONF = cfg.CONF
fake_neutron_port = {
"status": "test_port_status",
"status": "ACTIVE",
"allowed_address_pairs": [],
"admin_state_up": True,
"network_id": "test_net_id",
@ -132,7 +135,8 @@ class NeutronNetworkPluginTest(test.TestCase):
fake_share_network['project_id'],
network_id=fake_share_network['neutron_net_id'],
subnet_id=fake_share_network['neutron_subnet_id'],
device_owner='manila:share', host_id=None)
device_owner='manila:share',
device_id=fake_share_network['id'])
db_api.network_allocation_create.assert_called_once_with(
self.fake_context,
fake_network_allocation)
@ -141,54 +145,6 @@ class NeutronNetworkPluginTest(test.TestCase):
save_nw_data.stop()
save_subnet_data.stop()
@mock.patch.object(db_api, 'network_allocation_create',
mock.Mock(return_values=fake_network_allocation))
@mock.patch.object(db_api, 'share_network_get',
mock.Mock(return_value=fake_share_network))
@mock.patch.object(db_api, 'share_server_get',
mock.Mock(return_value=fake_share_server))
def test_allocate_network_host_id_prconfigured(self):
has_provider_nw_ext = mock.patch.object(
self.plugin, '_has_provider_network_extension').start()
has_provider_nw_ext.return_value = True
save_nw_data = mock.patch.object(self.plugin,
'_save_neutron_network_data').start()
save_subnet_data = mock.patch.object(
self.plugin,
'_save_neutron_subnet_data').start()
config_data = {
'DEFAULT': {
'neutron_host_id': 'fake_host_id',
}
}
with test_utils.create_temp_config_with_opts(config_data):
with mock.patch.object(self.plugin.neutron_api, 'create_port',
mock.Mock(return_value=fake_neutron_port)):
self.plugin.allocate_network(
self.fake_context,
fake_share_server,
fake_share_network,
allocation_info={'count': 1})
has_provider_nw_ext.assert_any_call()
save_nw_data.assert_called_once_with(self.fake_context,
fake_share_network)
save_subnet_data.assert_called_once_with(self.fake_context,
fake_share_network)
self.plugin.neutron_api.create_port.assert_called_once_with(
fake_share_network['project_id'],
network_id=fake_share_network['neutron_net_id'],
subnet_id=fake_share_network['neutron_subnet_id'],
device_owner='manila:share', host_id='fake_host_id')
db_api.network_allocation_create.assert_called_once_with(
self.fake_context,
fake_network_allocation)
has_provider_nw_ext.stop()
save_nw_data.stop()
save_subnet_data.stop()
@mock.patch.object(db_api, 'network_allocation_create',
mock.Mock(return_values=fake_network_allocation))
@mock.patch.object(db_api, 'share_network_get',
@ -217,11 +173,13 @@ class NeutronNetworkPluginTest(test.TestCase):
mock.call(fake_share_network['project_id'],
network_id=fake_share_network['neutron_net_id'],
subnet_id=fake_share_network['neutron_subnet_id'],
device_owner='manila:share', host_id=None),
device_owner='manila:share',
device_id=fake_share_network['id']),
mock.call(fake_share_network['project_id'],
network_id=fake_share_network['neutron_net_id'],
subnet_id=fake_share_network['neutron_subnet_id'],
device_owner='manila:share', host_id=None),
device_owner='manila:share',
device_id=fake_share_network['id']),
]
db_api_calls = [
mock.call(self.fake_context, fake_network_allocation),
@ -541,3 +499,559 @@ class NeutronSingleNetworkPluginTest(test.TestCase):
plugin.NeutronNetworkPlugin.allocate_network.assert_called_once_with(
self.context, share_server, share_network_upd, count=count,
device_owner=device_owner)
plugin.NeutronNetworkPlugin.allocate_network.reset_mock()
@ddt.ddt
class NeutronBindNetworkPluginTest(test.TestCase):
def setUp(self):
super(NeutronBindNetworkPluginTest, self).setUp()
self.fake_context = context.RequestContext(user_id='fake user',
project_id='fake project',
is_admin=False)
self.has_binding_ext_mock = self.mock_object(
neutron_api.API, '_has_port_binding_extension')
self.has_binding_ext_mock.return_value = True
self.bind_plugin = plugin.NeutronBindNetworkPlugin()
self.bind_plugin.db = db_api
self.sleep_mock = self.mock_object(time, 'sleep')
def test_wait_for_bind(self):
self.mock_object(self.bind_plugin.neutron_api, 'show_port')
self.bind_plugin.neutron_api.show_port.return_value = fake_neutron_port
self.bind_plugin._wait_for_ports_bind([fake_neutron_port],
fake_share_server)
self.bind_plugin.neutron_api.show_port.assert_called_once_with(
fake_neutron_port['id'])
self.sleep_mock.assert_not_called()
def test_wait_for_bind_error(self):
fake_neut_port = copy.copy(fake_neutron_port)
fake_neut_port['status'] = 'ERROR'
self.mock_object(self.bind_plugin.neutron_api, 'show_port')
self.bind_plugin.neutron_api.show_port.return_value = fake_neut_port
self.assertRaises(exception.ManilaException,
self.bind_plugin._wait_for_ports_bind,
[fake_neut_port, fake_neut_port],
fake_share_server)
self.bind_plugin.neutron_api.show_port.assert_called_once_with(
fake_neutron_port['id'])
self.sleep_mock.assert_not_called()
@ddt.data(('DOWN', 'ACTIVE'), ('DOWN', 'DOWN'), ('ACTIVE', 'DOWN'))
@mock.patch.object(time, 'time', side_effect=[1, 1, 3])
def test_wait_for_bind_two_ports_no_bind(self, state, time_mock):
fake_neut_port1 = copy.copy(fake_neutron_port)
fake_neut_port1['status'] = state[0]
fake_neut_port2 = copy.copy(fake_neutron_port)
fake_neut_port2['status'] = state[1]
self.mock_object(self.bind_plugin.neutron_api, 'show_port')
self.bind_plugin.neutron_api.show_port.side_effect = [fake_neut_port1,
fake_neut_port2]
self.assertRaises(exception.ManilaException,
self.bind_plugin._wait_for_ports_bind.__wrapped__,
self.bind_plugin,
[fake_neut_port1, fake_neut_port2],
fake_share_server)
@mock.patch.object(db_api, 'network_allocation_create',
mock.Mock(return_values=fake_network_allocation))
@mock.patch.object(db_api, 'share_network_get',
mock.Mock(return_value=fake_share_network))
@mock.patch.object(db_api, 'share_server_get',
mock.Mock(return_value=fake_share_server))
def test_allocate_network_one_allocation(self):
self.mock_object(self.bind_plugin, '_has_provider_network_extension')
self.bind_plugin._has_provider_network_extension.return_value = True
save_nw_data = self.mock_object(self.bind_plugin,
'_save_neutron_network_data')
save_subnet_data = self.mock_object(self.bind_plugin,
'_save_neutron_subnet_data')
self.mock_object(self.bind_plugin, '_wait_for_ports_bind')
neutron_host_id_opts = plugin.neutron_bind_network_plugin_opts[1]
self.mock_object(neutron_host_id_opts, 'default')
neutron_host_id_opts.default = 'foohost1'
self.mock_object(db_api, 'network_allocation_create')
with mock.patch.object(self.bind_plugin.neutron_api, 'create_port',
mock.Mock(return_value=fake_neutron_port)):
self.bind_plugin.allocate_network(
self.fake_context,
fake_share_server,
fake_share_network,
allocation_info={'count': 1})
self.bind_plugin._has_provider_network_extension.assert_any_call()
save_nw_data.assert_called_once_with(self.fake_context,
fake_share_network)
save_subnet_data.assert_called_once_with(self.fake_context,
fake_share_network)
expected_kwargs = {
'binding:vnic_type': 'baremetal',
'host_id': 'foohost1',
'network_id': fake_share_network['neutron_net_id'],
'subnet_id': fake_share_network['neutron_subnet_id'],
'device_owner': 'manila:share',
'device_id': fake_share_network['id'],
}
self.bind_plugin.neutron_api.create_port.assert_called_once_with(
fake_share_network['project_id'], **expected_kwargs)
db_api.network_allocation_create.assert_called_once_with(
self.fake_context,
fake_network_allocation)
self.bind_plugin._wait_for_ports_bind.assert_called_once_with(
[db_api.network_allocation_create()], fake_share_server)
@ddt.ddt
class NeutronBindSingleNetworkPluginTest(test.TestCase):
def setUp(self):
super(NeutronBindSingleNetworkPluginTest, self).setUp()
self.context = 'fake_context'
self.fake_context = context.RequestContext(user_id='fake user',
project_id='fake project',
is_admin=False)
self.has_binding_ext_mock = self.mock_object(
neutron_api.API, '_has_port_binding_extension')
self.has_binding_ext_mock.return_value = True
self.bind_plugin = plugin.NeutronBindNetworkPlugin()
self.bind_plugin.db = db_api
self.sleep_mock = self.mock_object(time, 'sleep')
fake_net_id = 'fake net id'
fake_subnet_id = 'fake subnet id'
config_data = {
'DEFAULT': {
'neutron_net_id': fake_net_id,
'neutron_subnet_id': fake_subnet_id,
}
}
fake_net = {'subnets': ['fake1', 'fake2', fake_subnet_id]}
self.mock_object(
neutron_api.API, 'get_network', mock.Mock(return_value=fake_net))
with test_utils.create_temp_config_with_opts(config_data):
self.bind_plugin = plugin.NeutronBindSingleNetworkPlugin()
self.bind_plugin.db = db_api
def test_init_valid(self):
fake_net_id = 'fake_net_id'
fake_subnet_id = 'fake_subnet_id'
config_data = {
'DEFAULT': {
'neutron_net_id': fake_net_id,
'neutron_subnet_id': fake_subnet_id,
}
}
fake_net = {'subnets': ['fake1', 'fake2', fake_subnet_id]}
self.mock_object(
neutron_api.API, 'get_network', mock.Mock(return_value=fake_net))
with test_utils.create_temp_config_with_opts(config_data):
instance = plugin.NeutronSingleNetworkPlugin()
self.assertEqual(fake_net_id, instance.net)
self.assertEqual(fake_subnet_id, instance.subnet)
neutron_api.API.get_network.assert_called_once_with(fake_net_id)
@ddt.data(
{'net': None, 'subnet': None},
{'net': 'fake_net_id', 'subnet': None},
{'net': None, 'subnet': 'fake_subnet_id'})
@ddt.unpack
def test_init_invalid(self, net, subnet):
config_data = dict()
# Simulate absence of set values
if net:
config_data['neutron_net_id'] = net
if subnet:
config_data['neutron_subnet_id'] = subnet
config_data = dict(DEFAULT=config_data)
with test_utils.create_temp_config_with_opts(config_data):
self.assertRaises(
exception.NetworkBadConfigurationException,
plugin.NeutronSingleNetworkPlugin)
@ddt.data({}, {'subnets': []}, {'subnets': ['different_foo_subnet']})
def test_init_subnet_does_not_belong_to_net(self, fake_net):
fake_net_id = 'fake_net_id'
config_data = {
'DEFAULT': {
'neutron_net_id': fake_net_id,
'neutron_subnet_id': 'fake_subnet_id',
}
}
self.mock_object(
neutron_api.API, 'get_network', mock.Mock(return_value=fake_net))
with test_utils.create_temp_config_with_opts(config_data):
self.assertRaises(
exception.NetworkBadConfigurationException,
plugin.NeutronSingleNetworkPlugin)
neutron_api.API.get_network.assert_called_once_with(fake_net_id)
def _get_neutron_single_network_plugin_instance(self):
fake_subnet_id = 'fake_subnet_id'
config_data = {
'DEFAULT': {
'neutron_net_id': 'fake_net_id',
'neutron_subnet_id': fake_subnet_id,
}
}
fake_net = {'subnets': [fake_subnet_id]}
self.mock_object(
neutron_api.API, 'get_network', mock.Mock(return_value=fake_net))
with test_utils.create_temp_config_with_opts(config_data):
instance = plugin.NeutronSingleNetworkPlugin()
return instance
def test___update_share_network_net_data_same_values(self):
instance = self._get_neutron_single_network_plugin_instance()
share_network = {
'neutron_net_id': instance.net,
'neutron_subnet_id': instance.subnet,
}
result = instance._update_share_network_net_data(
self.context, share_network)
self.assertEqual(share_network, result)
def test___update_share_network_net_data_different_values_empty(self):
instance = self._get_neutron_single_network_plugin_instance()
share_network_input = {
'id': 'fake_share_network_id',
}
share_network_result = {
'neutron_net_id': instance.net,
'neutron_subnet_id': instance.subnet,
}
self.mock_object(
instance.db, 'share_network_update',
mock.Mock(return_value='foo'))
instance._update_share_network_net_data(
self.context, share_network_input)
instance.db.share_network_update.assert_called_once_with(
self.context, share_network_input['id'], share_network_result)
@ddt.data(
{'n': 'fake_net_id', 's': 'bar'},
{'n': 'foo', 's': 'fake_subnet_id'})
@ddt.unpack
def test___update_share_network_net_data_different_values(self, n, s):
instance = self._get_neutron_single_network_plugin_instance()
share_network = {
'id': 'fake_share_network_id',
'neutron_net_id': n,
'neutron_subnet_id': s,
}
self.mock_object(
instance.db, 'share_network_update',
mock.Mock(return_value=share_network))
self.assertRaises(
exception.NetworkBadConfigurationException,
instance._update_share_network_net_data,
self.context, share_network)
self.assertFalse(instance.db.share_network_update.called)
def test___update_share_network_net_data_nova_net_id_present(self):
instance = self._get_neutron_single_network_plugin_instance()
share_network = {
'id': 'fake_share_network_id',
'nova_net_id': 'foo',
}
self.mock_object(
instance.db, 'share_network_update',
mock.Mock(return_value=share_network))
self.assertRaises(
exception.NetworkBadConfigurationException,
instance._update_share_network_net_data,
self.context, share_network)
self.assertFalse(instance.db.share_network_update.called)
@mock.patch.object(
plugin.NeutronNetworkPlugin, "allocate_network", mock.Mock())
def test_allocate_network(self):
instance = self._get_neutron_single_network_plugin_instance()
share_server = 'fake_share_server'
share_network = 'fake_share_network'
share_network_upd = 'updated_fake_share_network'
count = 2
device_owner = 'fake_device_owner'
self.mock_object(
instance, '_update_share_network_net_data',
mock.Mock(return_value=share_network_upd))
instance.allocate_network(
self.context, share_server, share_network, count=count,
device_owner=device_owner)
instance._update_share_network_net_data.assert_called_once_with(
self.context, share_network)
plugin.NeutronNetworkPlugin.allocate_network.assert_called_once_with(
self.context, share_server, share_network_upd, count=count,
device_owner=device_owner)
plugin.NeutronNetworkPlugin.allocate_network.reset_mock()
def test_wait_for_bind(self):
self.mock_object(self.bind_plugin.neutron_api, 'show_port')
self.bind_plugin.neutron_api.show_port.return_value = fake_neutron_port
self.bind_plugin._wait_for_ports_bind([fake_neutron_port],
fake_share_server)
self.bind_plugin.neutron_api.show_port.assert_called_once_with(
fake_neutron_port['id'])
self.sleep_mock.assert_not_called()
def test_wait_for_bind_error(self):
fake_neut_port = copy.copy(fake_neutron_port)
fake_neut_port['status'] = 'ERROR'
self.mock_object(self.bind_plugin.neutron_api, 'show_port')
self.bind_plugin.neutron_api.show_port.return_value = fake_neut_port
self.assertRaises(exception.ManilaException,
self.bind_plugin._wait_for_ports_bind,
[fake_neut_port, fake_neut_port],
fake_share_server)
self.bind_plugin.neutron_api.show_port.assert_called_once_with(
fake_neutron_port['id'])
self.sleep_mock.assert_not_called()
@ddt.data(('DOWN', 'ACTIVE'), ('DOWN', 'DOWN'), ('ACTIVE', 'DOWN'))
@mock.patch.object(time, 'time', side_effect=[1, 1, 3])
def test_wait_for_bind_two_ports_no_bind(self, state, time_mock):
fake_neut_port1 = copy.copy(fake_neutron_port)
fake_neut_port1['status'] = state[0]
fake_neut_port2 = copy.copy(fake_neutron_port)
fake_neut_port2['status'] = state[1]
self.mock_object(self.bind_plugin.neutron_api, 'show_port')
self.bind_plugin.neutron_api.show_port.side_effect = [fake_neut_port1,
fake_neut_port2]
self.assertRaises(exception.NetworkBindException,
self.bind_plugin._wait_for_ports_bind.__wrapped__,
self.bind_plugin,
[fake_neut_port1, fake_neut_port2],
fake_share_server)
@mock.patch.object(db_api, 'network_allocation_create',
mock.Mock(return_values=fake_network_allocation))
@mock.patch.object(db_api, 'share_network_get',
mock.Mock(return_value=fake_share_network))
@mock.patch.object(db_api, 'share_server_get',
mock.Mock(return_value=fake_share_server))
def test_allocate_network_one_allocation(self):
self.mock_object(self.bind_plugin, '_has_provider_network_extension')
self.bind_plugin._has_provider_network_extension.return_value = True
save_nw_data = self.mock_object(self.bind_plugin,
'_save_neutron_network_data')
save_subnet_data = self.mock_object(self.bind_plugin,
'_save_neutron_subnet_data')
self.mock_object(self.bind_plugin, '_wait_for_ports_bind')
neutron_host_id_opts = plugin.neutron_bind_network_plugin_opts[1]
self.mock_object(neutron_host_id_opts, 'default')
neutron_host_id_opts.default = 'foohost1'
self.mock_object(db_api, 'network_allocation_create')
with mock.patch.object(self.bind_plugin.neutron_api, 'create_port',
mock.Mock(return_value=fake_neutron_port)):
self.bind_plugin.allocate_network(
self.fake_context,
fake_share_server,
fake_share_network,
allocation_info={'count': 1})
self.bind_plugin._has_provider_network_extension.assert_any_call()
save_nw_data.assert_called_once_with(self.fake_context,
fake_share_network)
save_subnet_data.assert_called_once_with(self.fake_context,
fake_share_network)
expected_kwargs = {
'binding:vnic_type': 'baremetal',
'host_id': 'foohost1',
'network_id': fake_share_network['neutron_net_id'],
'subnet_id': fake_share_network['neutron_subnet_id'],
'device_owner': 'manila:share',
'device_id': fake_share_network['id'],
}
self.bind_plugin.neutron_api.create_port.assert_called_once_with(
fake_share_network['project_id'], **expected_kwargs)
db_api.network_allocation_create.assert_called_once_with(
self.fake_context,
fake_network_allocation)
self.bind_plugin._wait_for_ports_bind.assert_called_once_with(
[db_api.network_allocation_create()], fake_share_server)
class NeutronBindNetworkPluginWithNormalTypeTest(test.TestCase):
def setUp(self):
super(NeutronBindNetworkPluginWithNormalTypeTest, self).setUp()
config_data = {
'DEFAULT': {
'neutron_vnic_type': 'normal',
}
}
self.plugin = plugin.NeutronNetworkPlugin()
self.plugin.db = db_api
self.fake_context = context.RequestContext(user_id='fake user',
project_id='fake project',
is_admin=False)
with test_utils.create_temp_config_with_opts(config_data):
self.bind_plugin = plugin.NeutronBindNetworkPlugin()
self.bind_plugin.db = db_api
@mock.patch.object(db_api, 'network_allocation_create',
mock.Mock(return_values=fake_network_allocation))
@mock.patch.object(db_api, 'share_network_get',
mock.Mock(return_value=fake_share_network))
@mock.patch.object(db_api, 'share_server_get',
mock.Mock(return_value=fake_share_server))
def test_allocate_network_one_allocation(self):
self.mock_object(self.bind_plugin, '_has_provider_network_extension')
self.bind_plugin._has_provider_network_extension.return_value = True
save_nw_data = self.mock_object(self.bind_plugin,
'_save_neutron_network_data')
save_subnet_data = self.mock_object(self.bind_plugin,
'_save_neutron_subnet_data')
self.mock_object(self.bind_plugin, '_wait_for_ports_bind')
neutron_host_id_opts = plugin.neutron_bind_network_plugin_opts[1]
self.mock_object(neutron_host_id_opts, 'default')
neutron_host_id_opts.default = 'foohost1'
self.mock_object(db_api, 'network_allocation_create')
with mock.patch.object(self.bind_plugin.neutron_api, 'create_port',
mock.Mock(return_value=fake_neutron_port)):
self.bind_plugin.allocate_network(
self.fake_context,
fake_share_server,
fake_share_network,
allocation_info={'count': 1})
self.bind_plugin._has_provider_network_extension.assert_any_call()
save_nw_data.assert_called_once_with(self.fake_context,
fake_share_network)
save_subnet_data.assert_called_once_with(self.fake_context,
fake_share_network)
expected_kwargs = {
'binding:vnic_type': 'normal',
'host_id': 'foohost1',
'network_id': fake_share_network['neutron_net_id'],
'subnet_id': fake_share_network['neutron_subnet_id'],
'device_owner': 'manila:share',
'device_id': fake_share_network['id'],
}
self.bind_plugin.neutron_api.create_port.assert_called_once_with(
fake_share_network['project_id'], **expected_kwargs)
db_api.network_allocation_create.assert_called_once_with(
self.fake_context,
fake_network_allocation)
self.bind_plugin._wait_for_ports_bind.assert_not_called()
def test_update_network_allocation(self):
self.mock_object(self.bind_plugin, '_wait_for_ports_bind')
self.mock_object(db_api, 'network_allocations_get_for_share_server')
db_api.network_allocations_get_for_share_server.return_value = [
fake_neutron_port]
self.bind_plugin.update_network_allocation(self.fake_context,
fake_share_server)
self.bind_plugin._wait_for_ports_bind.assert_called_once_with(
[fake_neutron_port], fake_share_server)
class NeutronBindSingleNetworkPluginWithNormalTypeTest(test.TestCase):
def setUp(self):
super(NeutronBindSingleNetworkPluginWithNormalTypeTest, self).setUp()
fake_net_id = 'fake net id'
fake_subnet_id = 'fake subnet id'
config_data = {
'DEFAULT': {
'neutron_net_id': fake_net_id,
'neutron_subnet_id': fake_subnet_id,
'neutron_vnic_type': 'normal',
}
}
fake_net = {'subnets': ['fake1', 'fake2', fake_subnet_id]}
self.mock_object(
neutron_api.API, 'get_network', mock.Mock(return_value=fake_net))
self.plugin = plugin.NeutronNetworkPlugin()
self.plugin.db = db_api
self.fake_context = context.RequestContext(user_id='fake user',
project_id='fake project',
is_admin=False)
with test_utils.create_temp_config_with_opts(config_data):
self.bind_plugin = plugin.NeutronBindSingleNetworkPlugin()
self.bind_plugin.db = db_api
@mock.patch.object(db_api, 'network_allocation_create',
mock.Mock(return_values=fake_network_allocation))
@mock.patch.object(db_api, 'share_network_get',
mock.Mock(return_value=fake_share_network))
@mock.patch.object(db_api, 'share_server_get',
mock.Mock(return_value=fake_share_server))
def test_allocate_network_one_allocation(self):
self.mock_object(self.bind_plugin, '_has_provider_network_extension')
self.bind_plugin._has_provider_network_extension.return_value = True
save_nw_data = self.mock_object(self.bind_plugin,
'_save_neutron_network_data')
save_subnet_data = self.mock_object(self.bind_plugin,
'_save_neutron_subnet_data')
self.mock_object(self.bind_plugin, '_wait_for_ports_bind')
neutron_host_id_opts = plugin.neutron_bind_network_plugin_opts[1]
self.mock_object(neutron_host_id_opts, 'default')
neutron_host_id_opts.default = 'foohost1'
self.mock_object(db_api, 'network_allocation_create')
with mock.patch.object(self.bind_plugin.neutron_api, 'create_port',
mock.Mock(return_value=fake_neutron_port)):
self.bind_plugin.allocate_network(
self.fake_context,
fake_share_server,
fake_share_network,
allocation_info={'count': 1})
self.bind_plugin._has_provider_network_extension.assert_any_call()
save_nw_data.assert_called_once_with(self.fake_context,
fake_share_network)
save_subnet_data.assert_called_once_with(self.fake_context,
fake_share_network)
expected_kwargs = {
'binding:vnic_type': 'normal',
'host_id': 'foohost1',
'network_id': fake_share_network['neutron_net_id'],
'subnet_id': fake_share_network['neutron_subnet_id'],
'device_owner': 'manila:share',
'device_id': fake_share_network['id'],
}
self.bind_plugin.neutron_api.create_port.assert_called_once_with(
fake_share_network['project_id'], **expected_kwargs)
db_api.network_allocation_create.assert_called_once_with(
self.fake_context,
fake_network_allocation)
self.bind_plugin._wait_for_ports_bind.assert_not_called()
def test_update_network_allocation(self):
self.mock_object(self.bind_plugin, '_wait_for_ports_bind')
self.mock_object(db_api, 'network_allocations_get_for_share_server')
db_api.network_allocations_get_for_share_server.return_value = [
fake_neutron_port]
self.bind_plugin.update_network_allocation(self.fake_context,
fake_share_server)
self.bind_plugin._wait_for_ports_bind.assert_called_once_with(
[fake_neutron_port], fake_share_server)

View File

@ -0,0 +1,3 @@
---
features:
- Added neutron driver for port bind actions.