Add admin network for DHSS=True share drivers

For DHSS=True share drivers, the manager needs to provide an additional
pool of IP addresses (using an additional network plugin) so drivers
have a way to create export locations accessible on the admin network.
Admin network is useful for such feature as "share migration".

Implements bp admin-network-plugin

Change-Id: Id43b6ed1607b464970e6cd7281b5455d3fc6d2f3
This commit is contained in:
Valeriy Ponomaryov 2015-12-21 15:58:45 +02:00
parent 2e9fe58161
commit e8e8adc2be
21 changed files with 742 additions and 81 deletions

View File

@ -710,6 +710,9 @@ def share_network_remove_security_service(context, id, security_service_id):
security_service_id)
##################
def network_allocation_create(context, values):
"""Create a network allocation DB record."""
return IMPL.network_allocation_create(context, values)
@ -726,11 +729,10 @@ def network_allocation_update(context, id, values):
def network_allocations_get_for_share_server(context, share_server_id,
session=None):
"""Get network allocation for share server."""
return IMPL.network_allocations_get_for_share_server(context,
share_server_id,
session=session)
session=None, label=None):
"""Get network allocations for share server."""
return IMPL.network_allocations_get_for_share_server(
context, share_server_id, label=label, session=session)
def network_allocations_get_by_ip_address(context, ip_address):

View File

@ -0,0 +1,59 @@
# Copyright 2015 Mirantis 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.
"""Add more network info attributes to 'network_allocations' table.
Revision ID: 5155c7077f99
Revises: 293fac1130ca
Create Date: 2015-12-22 12:05:24.297049
"""
# revision identifiers, used by Alembic.
revision = '5155c7077f99'
down_revision = '293fac1130ca'
from alembic import op
import sqlalchemy as sa
def upgrade():
default_label_value = 'user'
op.add_column(
'network_allocations',
sa.Column('label',
sa.String(255),
default=default_label_value,
server_default=default_label_value,
nullable=True),
)
op.add_column(
'network_allocations',
sa.Column('network_type', sa.String(32), nullable=True))
op.add_column(
'network_allocations',
sa.Column('segmentation_id', sa.Integer, nullable=True))
op.add_column(
'network_allocations',
sa.Column('ip_version', sa.Integer, nullable=True))
op.add_column(
'network_allocations',
sa.Column('cidr', sa.String(64), nullable=True))
def downgrade():
for col_name in ('label', 'network_type', 'segmentation_id', 'ip_version',
'cidr'):
op.drop_column('network_allocations', col_name)

View File

@ -2942,11 +2942,26 @@ def network_allocations_get_by_ip_address(context, ip_address):
@require_context
def network_allocations_get_for_share_server(context, share_server_id,
session=None):
session=None, label=None):
if session is None:
session = get_session()
result = model_query(context, models.NetworkAllocation, session=session).\
filter_by(share_server_id=share_server_id).all()
query = model_query(
context, models.NetworkAllocation, session=session,
).filter_by(
share_server_id=share_server_id,
)
if label:
if label != 'admin':
query = query.filter(or_(
# NOTE(vponomaryov): we treat None as alias for 'user'.
models.NetworkAllocation.label == None, # noqa
models.NetworkAllocation.label == label,
))
else:
query = query.filter(models.NetworkAllocation.label == label)
result = query.all()
return result

View File

@ -807,7 +807,12 @@ class NetworkAllocation(BASE, ManilaBase):
__tablename__ = 'network_allocations'
id = Column(String(36), primary_key=True, nullable=False)
deleted = Column(String(36), default='False')
label = Column(String(255), nullable=True)
ip_address = Column(String(64), nullable=True)
ip_version = Column(Integer, nullable=True)
cidr = Column(String(64), nullable=True)
network_type = Column(String(32), nullable=True)
segmentation_id = Column(Integer, nullable=True)
mac_address = Column(String(32), nullable=True)
share_server_id = Column(String(36), ForeignKey('share_servers.id'),
nullable=False)

View File

@ -17,8 +17,11 @@ import abc
from oslo_config import cfg
from oslo_utils import importutils
import six
from manila.db import base as db_base
from manila import exception
from manila.i18n import _
network_opts = [
cfg.StrOpt(
@ -32,7 +35,7 @@ network_opts = [
CONF = cfg.CONF
def API(config_group_name=None):
def API(config_group_name=None, label='user'):
"""Selects class and config group of network plugin.
:param config_group_name: name of config group to be used for
@ -45,22 +48,28 @@ def API(config_group_name=None):
else:
network_api_class = CONF.network_api_class
cls = importutils.import_class(network_api_class)
return cls(config_group_name=config_group_name)
return cls(config_group_name=config_group_name, label=label)
@six.add_metaclass(abc.ABCMeta)
class NetworkBaseAPI(db_base.Base):
"""User network plugin for setting up main net interfaces."""
def __init__(self, db_driver=None):
super(NetworkBaseAPI, self).__init__(db_driver=db_driver)
def _verify_share_network(self, share_server_id, share_network):
if share_network is None:
msg = _("'Share network' is not provided for setting up "
"network interfaces for 'Share server' "
"'%s'.") % share_server_id
raise exception.NetworkBadConfigurationException(reason=msg)
@abc.abstractmethod
def allocate_network(self, context, network_id, subnet_id, **kwargs):
def allocate_network(self, context, share_server, share_network=None,
**kwargs):
pass
@abc.abstractmethod
def deallocate_network(self, context, share_server_id):
pass
@abc.abstractmethod
def get_provider_info(self, context, network_id, subnet_id):
pass

View File

@ -50,6 +50,11 @@ class NeutronNetworkPlugin(network.NetworkBaseAPI):
self._neutron_api = None
self._neutron_api_args = args
self._neutron_api_kwargs = kwargs
self._label = kwargs.pop('label', 'user')
@property
def label(self):
return self._label
@property
@utils.synchronized("instantiate_neutron_api")
@ -59,7 +64,8 @@ class NeutronNetworkPlugin(network.NetworkBaseAPI):
**self._neutron_api_kwargs)
return self._neutron_api
def allocate_network(self, context, share_server, share_network, **kwargs):
def allocate_network(self, context, share_server, share_network=None,
**kwargs):
"""Allocate network resources using given network information.
Create neutron ports for a given neutron network and subnet,
@ -77,6 +83,7 @@ class NeutronNetworkPlugin(network.NetworkBaseAPI):
msg = "%s extension required" % neutron_constants.PROVIDER_NW_EXT
raise exception.NetworkBadConfigurationException(reason=msg)
self._verify_share_network(share_server['id'], share_network)
self._save_neutron_network_data(context, share_network)
self._save_neutron_subnet_data(context, share_network)
@ -118,6 +125,11 @@ class NeutronNetworkPlugin(network.NetworkBaseAPI):
'ip_address': port['fixed_ips'][0]['ip_address'],
'mac_address': port['mac_address'],
'status': constants.STATUS_ACTIVE,
'label': self.label,
'network_type': share_network['network_type'],
'segmentation_id': share_network['segmentation_id'],
'ip_version': share_network['ip_version'],
'cidr': share_network['cidr'],
}
return self.db.network_allocation_create(context, port_dict)
@ -143,10 +155,11 @@ class NeutronNetworkPlugin(network.NetworkBaseAPI):
'network_type': net_info['provider:network_type'],
'segmentation_id': net_info['provider:segmentation_id']
}
share_network.update(provider_nw_dict)
self.db.share_network_update(context,
share_network['id'],
provider_nw_dict)
if self.label != 'admin':
self.db.share_network_update(
context, share_network['id'], provider_nw_dict)
def _save_neutron_subnet_data(self, context, share_network):
subnet_info = self.neutron_api.get_subnet(
@ -156,10 +169,11 @@ class NeutronNetworkPlugin(network.NetworkBaseAPI):
'cidr': subnet_info['cidr'],
'ip_version': subnet_info['ip_version']
}
share_network.update(subnet_values)
self.db.share_network_update(context,
share_network['id'],
subnet_values)
if self.label != 'admin':
self.db.share_network_update(
context, share_network['id'], subnet_values)
class NeutronSingleNetworkPlugin(NeutronNetworkPlugin):
@ -173,9 +187,17 @@ class NeutronSingleNetworkPlugin(NeutronNetworkPlugin):
self.subnet = self.neutron_api.configuration.neutron_subnet_id
self._verify_net_and_subnet()
def allocate_network(self, context, share_server, share_network, **kwargs):
share_network = self._update_share_network_net_data(
context, share_network)
def allocate_network(self, context, share_server, share_network=None,
**kwargs):
if self.label != 'admin':
share_network = self._update_share_network_net_data(
context, share_network)
else:
share_network = {
'project_id': self.neutron_api.admin_project_id,
'neutron_net_id': self.net,
'neutron_subnet_id': self.subnet,
}
super(NeutronSingleNetworkPlugin, self).allocate_network(
context, share_server, share_network, **kwargs)

View File

@ -44,12 +44,17 @@ class NovaNetworkPlugin(network.NetworkBaseAPI):
This plugin uses Nova networks provided within 'share-network' entities.
"""
def __init__(self, config_group_name=None, db_driver=None):
def __init__(self, config_group_name=None, db_driver=None, label=None):
super(NovaNetworkPlugin, self).__init__(db_driver=db_driver)
self.config_group_name = config_group_name or 'DEFAULT'
self._label = label or 'user'
self.admin_context = manila.context.get_admin_context()
self.nova_api = nova.API()
@property
def label(self):
return self._label
@utils.synchronized(
"allocate_network_for_nova_network_plugin", external=True)
def allocate_network(self, context, share_server, share_network, **kwargs):
@ -73,14 +78,20 @@ class NovaNetworkPlugin(network.NetworkBaseAPI):
# because several required attrs of network are available
# only for admins.
nova_net = self.nova_api.network_get(self.admin_context, nova_net_id)
self._save_network_info(context, nova_net, share_network['id'])
self._save_network_info(context, nova_net, share_network)
ip_addresses = self._get_available_ips(
context, nova_net, allocation_count)
for ip_address in ip_addresses:
data = dict(
share_server_id=share_server['id'],
ip_address=ip_address,
status=constants.STATUS_ACTIVE)
data = {
'share_server_id': share_server['id'],
'ip_address': ip_address,
'status': constants.STATUS_ACTIVE,
'label': self.label,
'cidr': share_network['cidr'],
'ip_version': share_network['ip_version'],
'segmentation_id': share_network['segmentation_id'],
'network_type': share_network['network_type'],
}
self.nova_api.fixed_ip_reserve(self.admin_context, ip_address)
allocations.append(
self.db.network_allocation_create(context, data))
@ -135,14 +146,17 @@ class NovaNetworkPlugin(network.NetworkBaseAPI):
LOG.error(msg)
raise exception.NetworkBadConfigurationException(reason=msg)
def _save_network_info(self, context, nova_net, share_network_id):
def _save_network_info(self, context, nova_net, share_network):
"""Update 'share-network' with plugin specific data."""
data = dict(
cidr=(nova_net['cidr'] or nova_net['cidr_v6']),
ip_version=(4 if nova_net['cidr'] else 6),
segmentation_id=nova_net['vlan'],
network_type=('vlan' if nova_net['vlan'] else 'flat'))
self.db.share_network_update(context, share_network_id, data)
data = {
'cidr': (nova_net['cidr'] or nova_net['cidr_v6']),
'ip_version': (4 if nova_net['cidr'] else 6),
'segmentation_id': nova_net['vlan'],
'network_type': ('vlan' if nova_net['vlan'] else 'flat'),
}
share_network.update(data)
if self.label != 'admin':
self.db.share_network_update(context, share_network['id'], data)
def deallocate_network(self, context, share_server_id):
"""Deallocate network resources for share server."""
@ -177,8 +191,11 @@ class NovaSingleNetworkPlugin(NovaNetworkPlugin):
@utils.synchronized(
"allocate_network_for_nova_network_plugin", external=True)
def allocate_network(self, context, share_server, share_network, **kwargs):
share_network = self._update_share_network_net_data(
context, share_network)
if self.label != 'admin':
share_network = self._update_share_network_net_data(
context, share_network)
else:
share_network = {'nova_net_id': self.net_id}
return self._allocate_network(
context, share_server, share_network, **kwargs)

View File

@ -43,7 +43,7 @@ standalone_network_plugin_opts = [
"share servers. Optional.",
choices=['flat', 'vlan', 'vxlan', 'gre'],
deprecated_group='DEFAULT'),
cfg.StrOpt(
cfg.IntOpt(
'standalone_network_plugin_segmentation_id',
help="Set it if network has segmentation (VLAN, VXLAN, etc...). "
"It will be assigned to share-network and share drivers will be "
@ -82,7 +82,7 @@ class StandaloneNetworkPlugin(network.NetworkBaseAPI):
from some network.
"""
def __init__(self, config_group_name=None, db_driver=None):
def __init__(self, config_group_name=None, db_driver=None, label='user'):
super(StandaloneNetworkPlugin, self).__init__(db_driver=db_driver)
self.config_group_name = config_group_name or 'DEFAULT'
CONF.register_opts(
@ -90,6 +90,7 @@ class StandaloneNetworkPlugin(network.NetworkBaseAPI):
group=self.config_group_name)
self.configuration = getattr(CONF, self.config_group_name, CONF)
self._set_persistent_network_data()
self._label = label
LOG.debug(
"\nStandalone network plugin data for config group "
"'%(config_group)s': \n"
@ -112,6 +113,10 @@ class StandaloneNetworkPlugin(network.NetworkBaseAPI):
ip_ranges=self.allowed_ip_ranges,
reserved=self.reserved_addresses))
@property
def label(self):
return self._label
def _set_persistent_network_data(self):
"""Sets persistent data for whole plugin."""
self.network_type = (
@ -240,30 +245,44 @@ class StandaloneNetworkPlugin(network.NetworkBaseAPI):
def _save_network_info(self, context, share_network):
"""Update share-network with plugin specific data."""
data = dict(
network_type=self.network_type,
segmentation_id=self.segmentation_id,
cidr=six.text_type(self.net.cidr),
ip_version=self.ip_version)
self.db.share_network_update(context, share_network['id'], data)
data = {
'network_type': self.network_type,
'segmentation_id': self.segmentation_id,
'cidr': six.text_type(self.net.cidr),
'ip_version': self.ip_version,
}
share_network.update(data)
if self.label != 'admin':
self.db.share_network_update(context, share_network['id'], data)
@utils.synchronized(
"allocate_network_for_standalone_network_plugin", external=True)
def allocate_network(self, context, share_server, share_network, **kwargs):
def allocate_network(self, context, share_server, share_network=None,
**kwargs):
"""Allocate network resources using one dedicated network.
This one has interprocess lock to avoid concurrency in creation of
share servers with same IP addresses using different share-networks.
"""
allocation_count = kwargs.get('count', 1)
if self.label != 'admin':
self._verify_share_network(share_server['id'], share_network)
else:
share_network = share_network or {}
self._save_network_info(context, share_network)
allocations = []
ip_addresses = self._get_available_ips(context, allocation_count)
for ip_address in ip_addresses:
data = dict(
share_server_id=share_server['id'],
ip_address=ip_address,
status=constants.STATUS_ACTIVE)
data = {
'share_server_id': share_server['id'],
'ip_address': ip_address,
'status': constants.STATUS_ACTIVE,
'label': self.label,
'network_type': share_network['network_type'],
'segmentation_id': share_network['segmentation_id'],
'cidr': share_network['cidr'],
'ip_version': share_network['ip_version'],
}
allocations.append(
self.db.network_allocation_create(context, data))
return allocations

View File

@ -111,6 +111,12 @@ share_opts = [
default=True,
help="Specify whether read only access mode is supported in this"
"backend."),
cfg.StrOpt(
"admin_network_config_group",
help="If share driver requires to setup admin network for share, then "
"define network plugin config options in some separate config "
"group and set its name here. Used only with another "
"option 'driver_handles_share_servers' set to 'True'."),
]
ssh_opts = [
@ -232,14 +238,25 @@ class ShareDriver(object):
self.configuration.append_config_values(share_opts)
network_config_group = (self.configuration.network_config_group or
self.configuration.config_group)
admin_network_config_group = (
self.configuration.admin_network_config_group)
else:
network_config_group = None
admin_network_config_group = (
CONF.admin_network_config_group)
self._verify_share_server_handling(driver_handles_share_servers)
if self.driver_handles_share_servers:
# Enable common network
self.network_api = network.API(
config_group_name=network_config_group)
# Enable admin network
if admin_network_config_group:
self._admin_network_api = network.API(
config_group_name=admin_network_config_group,
label='admin')
if hasattr(self, 'init_execute_mixin'):
# Instance with 'ExecuteMixin'
self.init_execute_mixin(*args, **kwargs) # pylint: disable=E1101
@ -247,6 +264,11 @@ class ShareDriver(object):
# Instance with 'GaneshaMixin'
self.init_ganesha_mixin(*args, **kwargs) # pylint: disable=E1101
@property
def admin_network_api(self):
if hasattr(self, '_admin_network_api'):
return self._admin_network_api
@property
def driver_handles_share_servers(self):
if self.configuration:
@ -675,6 +697,9 @@ class ShareDriver(object):
"""
raise NotImplementedError()
def get_admin_network_allocations_number(self):
return 0
def allocate_network(self, context, share_server, share_network,
count=None, **kwargs):
"""Allocate network resources using given network information."""
@ -685,6 +710,19 @@ class ShareDriver(object):
self.network_api.allocate_network(
context, share_server, share_network, **kwargs)
def allocate_admin_network(self, context, share_server, count=None,
**kwargs):
"""Allocate admin network resources using given network information."""
if count is None:
count = self.get_admin_network_allocations_number()
if count and not self.admin_network_api:
msg = _("Admin network plugin is not set up.")
raise exception.NetworkBadConfigurationException(reason=msg)
elif count:
kwargs.update(count=count)
self.admin_network_api.allocate_network(
context, share_server, **kwargs)
def deallocate_network(self, context, share_server_id):
"""Deallocate network resources for the given share server."""
if self.get_network_allocations_number():

View File

@ -1561,7 +1561,14 @@ class ShareManager(manager.SchedulerDependentManager):
# Network info is used by driver for setting up share server
# and getting server info on share creation.
network_allocations = self.db.network_allocations_get_for_share_server(
context, share_server['id'])
context, share_server['id'], label='user')
admin_network_allocations = (
self.db.network_allocations_get_for_share_server(
context, share_server['id'], label='admin'))
# NOTE(vponomaryov): following network_info fields are deprecated:
# 'segmentation_id', 'cidr' and 'network_type'.
# And they should be used from network allocations directly.
# They should be removed right after no one uses them.
network_info = {
'server_id': share_server['id'],
'segmentation_id': share_network['segmentation_id'],
@ -1571,6 +1578,7 @@ class ShareManager(manager.SchedulerDependentManager):
'nova_net_id': share_network['nova_net_id'],
'security_services': share_network['security_services'],
'network_allocations': network_allocations,
'admin_network_allocations': admin_network_allocations,
'backend_details': share_server.get('backend_details'),
'network_type': share_network['network_type'],
}
@ -1581,6 +1589,7 @@ class ShareManager(manager.SchedulerDependentManager):
share_network = self.db.share_network_get(
context, share_server['share_network_id'])
self.driver.allocate_network(context, share_server, share_network)
self.driver.allocate_admin_network(context, share_server)
# Get share_network again in case it was updated.
share_network = self.db.share_network_get(

View File

@ -470,3 +470,85 @@ class ShareReplicationMigrationChecks(BaseMigrationChecks):
self.valid_share_ids)
self.test_case.assertFalse(
hasattr(share_instance, 'replica_state'))
@map_to_migration('5155c7077f99')
class NetworkAllocationsNewLabelColumnChecks(BaseMigrationChecks):
table_name = 'network_allocations'
ids = ['fake_network_allocation_id_%d' % i for i in (1, 2, 3)]
def setup_upgrade_data(self, engine):
user_id = 'user_id'
project_id = 'project_id'
share_server_id = 'foo_share_server_id'
# Create share network
share_network_data = {
'id': 'foo_share_network_id',
'user_id': user_id,
'project_id': project_id,
}
sn_table = utils.load_table('share_networks', engine)
engine.execute(sn_table.insert(share_network_data))
# Create share server
share_server_data = {
'id': share_server_id,
'share_network_id': share_network_data['id'],
'host': 'fake_host',
'status': 'active',
}
ss_table = utils.load_table('share_servers', engine)
engine.execute(ss_table.insert(share_server_data))
# Create network allocations
network_allocations = [
{'id': self.ids[0],
'share_server_id': share_server_id,
'ip_address': '1.1.1.1'},
{'id': self.ids[1],
'share_server_id': share_server_id,
'ip_address': '2.2.2.2'},
]
na_table = utils.load_table(self.table_name, engine)
for network_allocation in network_allocations:
engine.execute(na_table.insert(network_allocation))
def check_upgrade(self, engine, data):
na_table = utils.load_table(self.table_name, engine)
for na in engine.execute(na_table.select()):
self.test_case.assertTrue(hasattr(na, 'label'))
self.test_case.assertEqual(na.label, 'user')
# Create admin network allocation
network_allocations = [
{'id': self.ids[2],
'share_server_id': na.share_server_id,
'ip_address': '3.3.3.3',
'label': 'admin',
'network_type': 'vlan',
'segmentation_id': 1005,
'ip_version': 4,
'cidr': '240.0.0.0/16'},
]
engine.execute(na_table.insert(network_allocations))
# Select admin network allocations
for na in engine.execute(
na_table.select().where(na_table.c.label == 'admin')):
self.test_case.assertTrue(hasattr(na, 'label'))
self.test_case.assertEqual('admin', na.label)
for col_name in ('network_type', 'segmentation_id', 'ip_version',
'cidr'):
self.test_case.assertTrue(hasattr(na, col_name))
self.test_case.assertEqual(
network_allocations[0][col_name], getattr(na, col_name))
def check_downgrade(self, engine):
na_table = utils.load_table(self.table_name, engine)
db_result = engine.execute(na_table.select())
self.test_case.assertTrue(db_result.rowcount >= len(self.ids))
for na in db_result:
for col_name in ('label', 'network_type', 'segmentation_id',
'ip_version', 'cidr'):
self.test_case.assertFalse(hasattr(na, col_name))

View File

@ -1967,3 +1967,99 @@ class AvailabilityZonesDatabaseAPITestCase(test.TestCase):
self.assertEqual(1, len(actual_result))
self.assertEqual('test2', actual_result[0]['name'])
@ddt.ddt
class NetworkAllocationsDatabaseAPITestCase(test.TestCase):
def setUp(self):
super(NetworkAllocationsDatabaseAPITestCase, self).setUp()
self.user_id = 'user_id'
self.project_id = 'project_id'
self.share_server_id = 'foo_share_server_id'
self.ctxt = context.RequestContext(
user_id=self.user_id, project_id=self.project_id, is_admin=True)
self.user_network_allocations = [
{'share_server_id': self.share_server_id,
'ip_address': '1.1.1.1',
'status': constants.STATUS_ACTIVE,
'label': None},
{'share_server_id': self.share_server_id,
'ip_address': '2.2.2.2',
'status': constants.STATUS_ACTIVE,
'label': 'user'},
]
self.admin_network_allocations = [
{'share_server_id': self.share_server_id,
'ip_address': '3.3.3.3',
'status': constants.STATUS_ACTIVE,
'label': 'admin'},
{'share_server_id': self.share_server_id,
'ip_address': '4.4.4.4',
'status': constants.STATUS_ACTIVE,
'label': 'admin'},
]
def _setup_network_allocations_get_for_share_server(self):
# Create share network
share_network_data = {
'id': 'foo_share_network_id',
'user_id': self.user_id,
'project_id': self.project_id,
}
db_api.share_network_create(self.ctxt, share_network_data)
# Create share server
share_server_data = {
'id': self.share_server_id,
'share_network_id': share_network_data['id'],
'host': 'fake_host',
'status': 'active',
}
db_api.share_server_create(self.ctxt, share_server_data)
# Create user network allocations
for user_network_allocation in self.user_network_allocations:
db_api.network_allocation_create(
self.ctxt, user_network_allocation)
# Create admin network allocations
for admin_network_allocation in self.admin_network_allocations:
db_api.network_allocation_create(
self.ctxt, admin_network_allocation)
def test_get_only_user_network_allocations(self):
self._setup_network_allocations_get_for_share_server()
result = db_api.network_allocations_get_for_share_server(
self.ctxt, self.share_server_id, label='user')
self.assertEqual(
len(self.user_network_allocations), len(result))
for na in result:
self.assertIn(na.label, (None, 'user'))
def test_get_only_admin_network_allocations(self):
self._setup_network_allocations_get_for_share_server()
result = db_api.network_allocations_get_for_share_server(
self.ctxt, self.share_server_id, label='admin')
self.assertEqual(
len(self.admin_network_allocations), len(result))
for na in result:
self.assertEqual(na.label, 'admin')
def test_get_all_network_allocations(self):
self._setup_network_allocations_get_for_share_server()
result = db_api.network_allocations_get_for_share_server(
self.ctxt, self.share_server_id, label=None)
self.assertEqual(
len(self.user_network_allocations +
self.admin_network_allocations),
len(result)
)
for na in result:
self.assertIn(na.label, ('admin', 'user', None))

View File

@ -57,6 +57,10 @@ fake_share_network = {
'name': 'fake name',
'description': 'fake description',
'security_services': [],
'network_type': 'fake_network_type',
'segmentation_id': 1234,
'ip_version': 4,
'cidr': 'fake_cidr',
}
fake_share_server = {
@ -73,6 +77,11 @@ fake_network_allocation = {
'ip_address': fake_neutron_port['fixed_ips'][0]['ip_address'],
'mac_address': fake_neutron_port['mac_address'],
'status': constants.STATUS_ACTIVE,
'label': 'user',
'network_type': fake_share_network['network_type'],
'segmentation_id': fake_share_network['segmentation_id'],
'ip_version': fake_share_network['ip_version'],
'cidr': fake_share_network['cidr'],
}

View File

@ -42,6 +42,8 @@ class NovaNetworkPluginTest(test.TestCase):
self.fake_context, self.share_server, share_network, count=0)
self.assertEqual([], allocations)
self.assertTrue(hasattr(self.instance, 'label'))
self.assertEqual('user', self.instance.label)
@ddt.data('flat', 'vlan')
def test_allocate_network_get_one(self, net_type):
@ -226,12 +228,12 @@ class NovaSingleNetworkPluginTest(test.TestCase):
self.context = context.RequestContext(
user_id='fake user', project_id='fake project', is_admin=False)
def _get_instance(self):
def _get_instance(self, label=None):
nova_net_id = 'fake_nova_net_id'
config_data = dict(
DEFAULT=dict(nova_single_network_plugin_net_id=nova_net_id))
with test_utils.create_temp_config_with_opts(config_data):
return plugin.NovaSingleNetworkPlugin()
return plugin.NovaSingleNetworkPlugin(label=label)
def test_init_valid(self):
nova_net_id = 'fake_nova_net_id'
@ -287,6 +289,24 @@ class NovaSingleNetworkPluginTest(test.TestCase):
instance._allocate_network.assert_called_once_with(
self.context, self.share_server, share_network, count=2)
def test_allocate_network_with_admin_label(self):
instance = self._get_instance(label='admin')
allocations = ['foo', 'bar']
self.mock_object(instance.db, 'share_network_update')
self.mock_object(
instance, '_allocate_network', mock.Mock(return_value=allocations))
fake_share_network = {'nova_net_id': 'fake_nova_net_id'}
result = instance.allocate_network(
self.context, self.share_server, fake_share_network, count=2)
self.assertTrue(hasattr(instance, 'label'))
self.assertEqual('admin', instance.label)
self.assertEqual(allocations, result)
instance.db.share_network_update.assert_has_calls([])
instance._allocate_network.assert_called_once_with(
self.context, self.share_server, fake_share_network, count=2)
def test_allocate_network_different_nova_net_id_is_set(self):
instance = self._get_instance()
share_network = dict(

View File

@ -67,7 +67,7 @@ class StandaloneNetworkPluginTest(test.TestCase):
'standalone_network_plugin_gateway': '10.0.0.1',
'standalone_network_plugin_mask': '255.255.0.0',
'standalone_network_plugin_network_type': 'vlan',
'standalone_network_plugin_segmentation_id': '1001',
'standalone_network_plugin_segmentation_id': 1001,
'standalone_network_plugin_allowed_ip_ranges': (
'10.0.0.3-10.0.0.7,10.0.0.69-10.0.0.157,10.0.0.213'),
'standalone_network_plugin_ip_version': 4,
@ -86,7 +86,7 @@ class StandaloneNetworkPluginTest(test.TestCase):
self.assertEqual('10.0.0.1', instance.gateway)
self.assertEqual('255.255.0.0', instance.mask)
self.assertEqual('vlan', instance.network_type)
self.assertEqual('1001', instance.segmentation_id)
self.assertEqual(1001, instance.segmentation_id)
self.assertEqual(allowed_cidrs, instance.allowed_cidrs)
self.assertEqual(
['10.0.0.3-10.0.0.7', '10.0.0.69-10.0.0.157', '10.0.0.213'],
@ -134,7 +134,7 @@ class StandaloneNetworkPluginTest(test.TestCase):
'standalone_network_plugin_gateway': '2001:db8::0001',
'standalone_network_plugin_mask': '88',
'standalone_network_plugin_network_type': 'vlan',
'standalone_network_plugin_segmentation_id': '3999',
'standalone_network_plugin_segmentation_id': 3999,
'standalone_network_plugin_allowed_ip_ranges': (
'2001:db8::-2001:db8:0000:0000:0000:007f:ffff:ffff'),
'standalone_network_plugin_ip_version': 6,
@ -148,7 +148,7 @@ class StandaloneNetworkPluginTest(test.TestCase):
self.assertEqual('2001:db8::0001', instance.gateway)
self.assertEqual('88', instance.mask)
self.assertEqual('vlan', instance.network_type)
self.assertEqual('3999', instance.segmentation_id)
self.assertEqual(3999, instance.segmentation_id)
self.assertEqual(['2001:db8::/89'], instance.allowed_cidrs)
self.assertEqual(
['2001:db8::-2001:db8:0000:0000:0000:007f:ffff:ffff'],
@ -166,7 +166,7 @@ class StandaloneNetworkPluginTest(test.TestCase):
'standalone_network_plugin_gateway': '10.0.0.1',
'standalone_network_plugin_mask': '255.255.0.0',
'standalone_network_plugin_network_type': network_type,
'standalone_network_plugin_segmentation_id': '1001',
'standalone_network_plugin_segmentation_id': 1001,
'standalone_network_plugin_ip_version': 4,
},
}
@ -184,7 +184,7 @@ class StandaloneNetworkPluginTest(test.TestCase):
'standalone_network_plugin_gateway': '10.0.0.1',
'standalone_network_plugin_mask': '255.255.0.0',
'standalone_network_plugin_network_type': fake_network_type,
'standalone_network_plugin_segmentation_id': '1001',
'standalone_network_plugin_segmentation_id': 1001,
'standalone_network_plugin_ip_version': 4,
},
}
@ -335,7 +335,7 @@ class StandaloneNetworkPluginTest(test.TestCase):
data = {
'DEFAULT': {
'standalone_network_plugin_network_type': 'vlan',
'standalone_network_plugin_segmentation_id': '1003',
'standalone_network_plugin_segmentation_id': 1003,
'standalone_network_plugin_gateway': '10.0.0.1',
'standalone_network_plugin_mask': '24',
},
@ -352,16 +352,21 @@ class StandaloneNetworkPluginTest(test.TestCase):
fake_context, fake_share_server, fake_share_network)
self.assertEqual(1, len(allocations))
na_data = {
'network_type': 'vlan',
'segmentation_id': 1003,
'cidr': '10.0.0.0/24',
'ip_version': 4,
}
instance.db.share_network_update.assert_called_once_with(
fake_context, fake_share_network['id'],
dict(network_type='vlan', segmentation_id='1003',
cidr=six.text_type(instance.net.cidr), ip_version=4))
fake_context, fake_share_network['id'], na_data)
instance.db.network_allocations_get_by_ip_address.assert_has_calls(
[mock.call(fake_context, '10.0.0.2')])
instance.db.network_allocation_create.assert_called_once_with(
fake_context,
dict(share_server_id=fake_share_server['id'],
ip_address='10.0.0.2', status=constants.STATUS_ACTIVE))
ip_address='10.0.0.2', status=constants.STATUS_ACTIVE,
label='user', **na_data))
def test_allocate_network_two_ip_addresses_ipv4_two_usages_exist(self):
ctxt = type('FakeCtxt', (object,), {'fake': ['10.0.0.2', '10.0.0.4']})
@ -391,10 +396,14 @@ class StandaloneNetworkPluginTest(test.TestCase):
ctxt, fake_share_server, fake_share_network, count=2)
self.assertEqual(2, len(allocations))
na_data = {
'network_type': None,
'segmentation_id': None,
'cidr': six.text_type(instance.net.cidr),
'ip_version': 4,
}
instance.db.share_network_update.assert_called_once_with(
ctxt, fake_share_network['id'],
dict(network_type=None, segmentation_id=None,
cidr=six.text_type(instance.net.cidr), ip_version=4))
ctxt, fake_share_network['id'], dict(**na_data))
instance.db.network_allocations_get_by_ip_address.assert_has_calls(
[mock.call(ctxt, '10.0.0.2'), mock.call(ctxt, '10.0.0.3'),
mock.call(ctxt, '10.0.0.4'), mock.call(ctxt, '10.0.0.5')])
@ -402,11 +411,13 @@ class StandaloneNetworkPluginTest(test.TestCase):
mock.call(
ctxt,
dict(share_server_id=fake_share_server['id'],
ip_address='10.0.0.3', status=constants.STATUS_ACTIVE)),
ip_address='10.0.0.3', status=constants.STATUS_ACTIVE,
label='user', **na_data)),
mock.call(
ctxt,
dict(share_server_id=fake_share_server['id'],
ip_address='10.0.0.5', status=constants.STATUS_ACTIVE)),
ip_address='10.0.0.5', status=constants.STATUS_ACTIVE,
label='user', **na_data)),
])
def test_allocate_network_no_available_ipv4_addresses(self):

View File

@ -49,6 +49,8 @@ class HPE3ParDriverTestCase(test.TestCase):
self.conf.hpe3par_fstore_per_share = False
self.conf.hpe3par_require_cifs_ip = False
self.conf.network_config_group = 'test_network_config_group'
self.conf.admin_network_config_group = (
'test_admin_network_config_group')
def safe_get(attr):
try:

View File

@ -712,6 +712,8 @@ class HuaweiShareDriverTestCase(test.TestCase):
self.configuration = mock.Mock(spec=conf.Configuration)
self.configuration.safe_get = mock.Mock(side_effect=_safe_get)
self.configuration.network_config_group = 'fake_network_config_group'
self.configuration.admin_network_config_group = (
'fake_admin_network_config_group')
self.configuration.share_backend_name = 'fake_share_backend_name'
self.configuration.huawei_share_backend = 'V3'
self.configuration.max_over_subscription_ratio = 1

View File

@ -89,6 +89,8 @@ class ZFSSAShareDriverTestCase(test.TestCase):
self.configuration.zfssa_nas_quota_snap = 'true'
self.configuration.zfssa_rest_timeout = 60
self.configuration.network_config_group = 'fake_network_config_group'
self.configuration.admin_network_config_group = (
'fake_admin_network_config_group')
self.configuration.driver_handles_share_servers = False
def test_create_share(self):

View File

@ -72,12 +72,15 @@ class ShareDriverTestCase(test.TestCase):
self.assertTrue(share_driver.driver_handles_share_servers)
def _instantiate_share_driver(self, network_config_group,
driver_handles_share_servers):
driver_handles_share_servers,
admin_network_config_group=None):
self.mock_object(network, 'API')
config = mock.Mock()
config.append_config_values = mock.Mock()
config.config_group = 'fake_config_group'
config.network_config_group = network_config_group
if admin_network_config_group:
config.admin_network_config_group = admin_network_config_group
config.safe_get = mock.Mock(return_value=driver_handles_share_servers)
share_driver = driver.ShareDriver([True, False], configuration=config)
@ -85,14 +88,26 @@ class ShareDriverTestCase(test.TestCase):
self.assertTrue(hasattr(share_driver, 'configuration'))
config.append_config_values.assert_called_once_with(driver.share_opts)
if driver_handles_share_servers:
calls = []
if network_config_group:
network.API.assert_called_once_with(
config_group_name=config.network_config_group)
calls.append(mock.call(
config_group_name=config.network_config_group))
else:
network.API.assert_called_once_with(
config_group_name=config.config_group)
calls.append(mock.call(
config_group_name=config.config_group))
if admin_network_config_group:
calls.append(mock.call(
config_group_name=config.admin_network_config_group,
label='admin'))
network.API.assert_has_calls(calls)
self.assertTrue(hasattr(share_driver, 'network_api'))
self.assertTrue(hasattr(share_driver, 'admin_network_api'))
self.assertIsNotNone(share_driver.network_api)
self.assertIsNotNone(share_driver.admin_network_api)
else:
self.assertFalse(hasattr(share_driver, 'network_api'))
self.assertTrue(hasattr(share_driver, 'admin_network_api'))
self.assertIsNone(share_driver.admin_network_api)
self.assertFalse(network.API.called)
return share_driver
@ -102,6 +117,11 @@ class ShareDriverTestCase(test.TestCase):
def test_instantiate_share_driver_another_config_group(self):
self._instantiate_share_driver("fake_network_config_group", True)
def test_instantiate_share_driver_with_admin_network(self):
self._instantiate_share_driver(
"fake_network_config_group", True,
"fake_admin_network_config_group")
def test_instantiate_share_driver_no_configuration(self):
self.mock_object(network, 'API')
@ -358,6 +378,87 @@ class ShareDriverTestCase(test.TestCase):
self.assertEqual(share_instances, result)
def test_get_admin_network_allocations_number(self):
share_driver = self._instantiate_share_driver(None, True)
self.assertEqual(
0, share_driver.get_admin_network_allocations_number())
def test_allocate_admin_network_count_None(self):
share_driver = self._instantiate_share_driver(None, True)
ctxt = 'fake_context'
share_server = 'fake_share_server'
mock_get_admin_network_allocations_number = self.mock_object(
share_driver,
'get_admin_network_allocations_number',
mock.Mock(return_value=0))
self.mock_object(
share_driver.admin_network_api,
'allocate_network',
mock.Mock(side_effect=Exception('ShouldNotBeRaised')))
share_driver.allocate_admin_network(ctxt, share_server)
mock_get_admin_network_allocations_number.assert_called_once_with()
self.assertFalse(
share_driver.admin_network_api.allocate_network.called)
def test_allocate_admin_network_count_0(self):
share_driver = self._instantiate_share_driver(None, True)
ctxt = 'fake_context'
share_server = 'fake_share_server'
self.mock_object(
share_driver,
'get_admin_network_allocations_number',
mock.Mock(return_value=0))
self.mock_object(
share_driver.admin_network_api,
'allocate_network',
mock.Mock(side_effect=Exception('ShouldNotBeRaised')))
share_driver.allocate_admin_network(ctxt, share_server, count=0)
self.assertFalse(
share_driver.get_admin_network_allocations_number.called)
self.assertFalse(
share_driver.admin_network_api.allocate_network.called)
def test_allocate_admin_network_count_1_api_initialized(self):
share_driver = self._instantiate_share_driver(None, True)
ctxt = 'fake_context'
share_server = 'fake_share_server'
mock_get_admin_network_allocations_number = self.mock_object(
share_driver,
'get_admin_network_allocations_number',
mock.Mock(return_value=1))
self.mock_object(
share_driver.admin_network_api,
'allocate_network',
mock.Mock())
share_driver.allocate_admin_network(ctxt, share_server)
mock_get_admin_network_allocations_number.assert_called_once_with()
share_driver.admin_network_api.allocate_network.\
assert_called_once_with(ctxt, share_server, count=1)
def test_allocate_admin_network_count_1_api_not_initialized(self):
share_driver = self._instantiate_share_driver(None, True, None)
ctxt = 'fake_context'
share_server = 'fake_share_server'
share_driver._admin_network_api = None
mock_get_admin_network_allocations_number = self.mock_object(
share_driver,
'get_admin_network_allocations_number',
mock.Mock(return_value=1))
self.assertRaises(
exception.NetworkBadConfigurationException,
share_driver.allocate_admin_network,
ctxt, share_server,
)
mock_get_admin_network_allocations_number.assert_called_once_with()
def test_migrate_share(self):
driver.CONF.set_default('driver_handles_share_servers', False)

View File

@ -2311,10 +2311,15 @@ class ShareManagerTestCase(test.TestCase):
self.assertEqual(1, mock_LOG.error.call_count)
def test__form_server_setup_info(self):
fake_network_allocations = ['foo', 'bar']
def fake_network_allocations_get_for_share_server(*args, **kwargs):
if kwargs.get('label') != 'admin':
return ['foo', 'bar']
return ['admin-foo', 'admin-bar']
self.mock_object(
self.share_manager.db, 'network_allocations_get_for_share_server',
mock.Mock(return_value=fake_network_allocations))
mock.Mock(
side_effect=fake_network_allocations_get_for_share_server))
fake_share_server = dict(
id='fake_share_server_id', backend_details=dict(foo='bar'))
fake_share_network = dict(
@ -2333,7 +2338,10 @@ class ShareManagerTestCase(test.TestCase):
neutron_subnet_id=fake_share_network['neutron_subnet_id'],
nova_net_id=fake_share_network['nova_net_id'],
security_services=fake_share_network['security_services'],
network_allocations=fake_network_allocations,
network_allocations=(
fake_network_allocations_get_for_share_server()),
admin_network_allocations=(
fake_network_allocations_get_for_share_server(label='admin')),
backend_details=fake_share_server['backend_details'],
network_type=fake_share_network['network_type'])
@ -2342,7 +2350,10 @@ class ShareManagerTestCase(test.TestCase):
self.assertEqual(expected, network_info)
self.share_manager.db.network_allocations_get_for_share_server.\
assert_called_once_with(self.context, fake_share_server['id'])
assert_has_calls([
mock.call(self.context, fake_share_server['id'], label='user'),
mock.call(self.context, fake_share_server['id'], label='admin')
])
@ddt.data(
{'network_info': {'network_type': 'vlan', 'segmentation_id': '100'}},

View File

@ -0,0 +1,130 @@
# Copyright 2015 Mirantis 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 ddt
from oslo_config import cfg
from oslo_utils import importutils
from manila import exception
from manila import network
from manila import test
CONF = cfg.CONF
@ddt.ddt
class APITestCase(test.TestCase):
def setUp(self):
super(self.__class__, self).setUp()
self.mock_object(importutils, 'import_class')
def test_init_api_with_default_config_group_name(self):
network.API()
importutils.import_class.assert_called_once_with(
CONF.network_api_class)
importutils.import_class.return_value.assert_called_once_with(
config_group_name=None, label='user')
def test_init_api_with_custom_config_group_name(self):
group_name = 'FOO_GROUP_NAME'
network.API(config_group_name=group_name)
importutils.import_class.assert_called_once_with(
getattr(CONF, group_name).network_api_class)
importutils.import_class.return_value.assert_called_once_with(
config_group_name=group_name, label='user')
def test_init_api_with_custom_config_group_name_and_label(self):
group_name = 'FOO_GROUP_NAME'
label = 'custom_label'
network.API(config_group_name=group_name, label=label)
importutils.import_class.assert_called_once_with(
getattr(CONF, group_name).network_api_class)
importutils.import_class.return_value.assert_called_once_with(
config_group_name=group_name, label=label)
@ddt.ddt
class NetworkBaseAPITestCase(test.TestCase):
def setUp(self):
super(self.__class__, self).setUp()
self.db_driver = 'fake_driver'
self.mock_object(importutils, 'import_module')
def test_inherit_network_base_api_no_redefinitions(self):
class FakeNetworkAPI(network.NetworkBaseAPI):
pass
self.assertRaises(TypeError, FakeNetworkAPI)
def test_inherit_network_base_api_deallocate_not_redefined(self):
class FakeNetworkAPI(network.NetworkBaseAPI):
def allocate_network(self, *args, **kwargs):
pass
self.assertRaises(TypeError, FakeNetworkAPI)
def test_inherit_network_base_api_allocate_not_redefined(self):
class FakeNetworkAPI(network.NetworkBaseAPI):
def deallocate_network(self, *args, **kwargs):
pass
self.assertRaises(TypeError, FakeNetworkAPI)
def test_inherit_network_base_api(self):
class FakeNetworkAPI(network.NetworkBaseAPI):
def allocate_network(self, *args, **kwargs):
pass
def deallocate_network(self, *args, **kwargs):
pass
result = FakeNetworkAPI()
self.assertTrue(hasattr(result, '_verify_share_network'))
self.assertTrue(hasattr(result, 'allocate_network'))
self.assertTrue(hasattr(result, 'deallocate_network'))
def test__verify_share_network_ok(self):
class FakeNetworkAPI(network.NetworkBaseAPI):
def allocate_network(self, *args, **kwargs):
pass
def deallocate_network(self, *args, **kwargs):
pass
result = FakeNetworkAPI()
result._verify_share_network('foo_id', {'id': 'bar_id'})
def test__verify_share_network_fail(self):
class FakeNetworkAPI(network.NetworkBaseAPI):
def allocate_network(self, *args, **kwargs):
pass
def deallocate_network(self, *args, **kwargs):
pass
result = FakeNetworkAPI()
self.assertRaises(
exception.NetworkBadConfigurationException,
result._verify_share_network, 'foo_id', None)