Enable IPv6 in manila(network plugins and drivers)
Please read spec for design detail [1]. Support IPv6 in IP drivers, networks and share type extra specs. Co-Authored-By: TommyLikeHu(tommylikehu@gmail.com) Co-Authored-By: Ben Swartzlander <ben@swartzlander.org> [1] f7202a6cfe32a057f752a4e393f848f8a0211c36 DocImpact Partial-Implements: blueprint support-ipv6-access Change-Id: I96d3389262e9829b8b4344870cdf5c76abd22828
This commit is contained in:
parent
9cc5c3bf56
commit
2b40e5618f
@ -32,6 +32,19 @@ network_opts = [
|
|||||||
help='The full class name of the Networking API class to use.'),
|
help='The full class name of the Networking API class to use.'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
network_base_opts = [
|
||||||
|
cfg.BoolOpt(
|
||||||
|
'network_plugin_ipv4_enabled',
|
||||||
|
default=True,
|
||||||
|
help="Whether to support IPv4 network resource, Default=True."),
|
||||||
|
cfg.BoolOpt(
|
||||||
|
'network_plugin_ipv6_enabled',
|
||||||
|
default=False,
|
||||||
|
help="Whether to support IPv6 network resource, Default=False. "
|
||||||
|
"If this option is True, the value of "
|
||||||
|
"'network_plugin_ipv4_enabled' will be ignored."),
|
||||||
|
]
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
|
|
||||||
|
|
||||||
@ -55,7 +68,14 @@ def API(config_group_name=None, label='user'):
|
|||||||
class NetworkBaseAPI(db_base.Base):
|
class NetworkBaseAPI(db_base.Base):
|
||||||
"""User network plugin for setting up main net interfaces."""
|
"""User network plugin for setting up main net interfaces."""
|
||||||
|
|
||||||
def __init__(self, db_driver=None):
|
def __init__(self, config_group_name=None, db_driver=None):
|
||||||
|
if config_group_name:
|
||||||
|
CONF.register_opts(network_base_opts,
|
||||||
|
group=config_group_name)
|
||||||
|
else:
|
||||||
|
CONF.register_opts(network_base_opts)
|
||||||
|
self.configuration = getattr(CONF,
|
||||||
|
six.text_type(config_group_name), CONF)
|
||||||
super(NetworkBaseAPI, self).__init__(db_driver=db_driver)
|
super(NetworkBaseAPI, self).__init__(db_driver=db_driver)
|
||||||
|
|
||||||
def _verify_share_network(self, share_server_id, share_network):
|
def _verify_share_network(self, share_server_id, share_network):
|
||||||
@ -84,3 +104,17 @@ class NetworkBaseAPI(db_base.Base):
|
|||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def deallocate_network(self, context, share_server_id):
|
def deallocate_network(self, context, share_server_id):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
def enabled_ip_version(self):
|
||||||
|
if not hasattr(self, '_enabled_ip_version'):
|
||||||
|
if self.configuration.network_plugin_ipv6_enabled:
|
||||||
|
self._enabled_ip_version = 6
|
||||||
|
elif self.configuration.network_plugin_ipv4_enabled:
|
||||||
|
self._enabled_ip_version = 4
|
||||||
|
else:
|
||||||
|
msg = _("Either 'network_plugin_ipv4_enabled' or "
|
||||||
|
"'network_plugin_ipv6_enabled' "
|
||||||
|
"should be configured to 'True'.")
|
||||||
|
raise exception.NetworkBadConfigurationException(reason=msg)
|
||||||
|
return self._enabled_ip_version
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import ipaddress
|
||||||
|
import six
|
||||||
import socket
|
import socket
|
||||||
|
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
@ -99,7 +101,10 @@ class NeutronNetworkPlugin(network.NetworkBaseAPI):
|
|||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
db_driver = kwargs.pop('db_driver', None)
|
db_driver = kwargs.pop('db_driver', None)
|
||||||
super(NeutronNetworkPlugin, self).__init__(db_driver=db_driver)
|
config_group_name = kwargs.get('config_group_name', 'DEFAULT')
|
||||||
|
super(NeutronNetworkPlugin,
|
||||||
|
self).__init__(config_group_name=config_group_name,
|
||||||
|
db_driver=db_driver)
|
||||||
self._neutron_api = None
|
self._neutron_api = None
|
||||||
self._neutron_api_args = args
|
self._neutron_api_args = args
|
||||||
self._neutron_api_kwargs = kwargs
|
self._neutron_api_kwargs = kwargs
|
||||||
@ -156,6 +161,23 @@ class NeutronNetworkPlugin(network.NetworkBaseAPI):
|
|||||||
|
|
||||||
return ports
|
return ports
|
||||||
|
|
||||||
|
def _get_matched_ip_address(self, fixed_ips, ip_version):
|
||||||
|
"""Get first ip address which matches the specified ip_version."""
|
||||||
|
|
||||||
|
for ip in fixed_ips:
|
||||||
|
try:
|
||||||
|
address = ipaddress.ip_address(six.text_type(ip['ip_address']))
|
||||||
|
if address.version == ip_version:
|
||||||
|
return ip['ip_address']
|
||||||
|
except ValueError:
|
||||||
|
LOG.error("%(address)s isn't a valid ip "
|
||||||
|
"address, omitted."), {'address':
|
||||||
|
ip['ip_address']}
|
||||||
|
msg = _("Can not find any IP address with configured IP "
|
||||||
|
"version %(version)s in share-network.") % {'version':
|
||||||
|
ip_version}
|
||||||
|
raise exception.NetworkBadConfigurationException(reason=msg)
|
||||||
|
|
||||||
def deallocate_network(self, context, share_server_id):
|
def deallocate_network(self, context, share_server_id):
|
||||||
"""Deallocate neutron network resources for the given share server.
|
"""Deallocate neutron network resources for the given share server.
|
||||||
|
|
||||||
@ -188,10 +210,12 @@ class NeutronNetworkPlugin(network.NetworkBaseAPI):
|
|||||||
port = self.neutron_api.create_port(
|
port = self.neutron_api.create_port(
|
||||||
share_network['project_id'], **create_args)
|
share_network['project_id'], **create_args)
|
||||||
|
|
||||||
|
ip_address = self._get_matched_ip_address(port['fixed_ips'],
|
||||||
|
share_network['ip_version'])
|
||||||
port_dict = {
|
port_dict = {
|
||||||
'id': port['id'],
|
'id': port['id'],
|
||||||
'share_server_id': share_server['id'],
|
'share_server_id': share_server['id'],
|
||||||
'ip_address': port['fixed_ips'][0]['ip_address'],
|
'ip_address': ip_address,
|
||||||
'gateway': share_network['gateway'],
|
'gateway': share_network['gateway'],
|
||||||
'mac_address': port['mac_address'],
|
'mac_address': port['mac_address'],
|
||||||
'status': constants.STATUS_ACTIVE,
|
'status': constants.STATUS_ACTIVE,
|
||||||
|
@ -27,7 +27,7 @@ from manila import utils
|
|||||||
standalone_network_plugin_opts = [
|
standalone_network_plugin_opts = [
|
||||||
cfg.StrOpt(
|
cfg.StrOpt(
|
||||||
'standalone_network_plugin_gateway',
|
'standalone_network_plugin_gateway',
|
||||||
help="Gateway IPv4 address that should be used. Required.",
|
help="Gateway address that should be used. Required.",
|
||||||
deprecated_group='DEFAULT'),
|
deprecated_group='DEFAULT'),
|
||||||
cfg.StrOpt(
|
cfg.StrOpt(
|
||||||
'standalone_network_plugin_mask',
|
'standalone_network_plugin_mask',
|
||||||
@ -63,7 +63,12 @@ standalone_network_plugin_opts = [
|
|||||||
'standalone_network_plugin_ip_version',
|
'standalone_network_plugin_ip_version',
|
||||||
default=4,
|
default=4,
|
||||||
help="IP version of network. Optional."
|
help="IP version of network. Optional."
|
||||||
"Allowed values are '4' and '6'. Default value is '4'.",
|
"Allowed values are '4' and '6'. Default value is '4'. "
|
||||||
|
"Note: This option is no longer used and has no effect",
|
||||||
|
deprecated_for_removal=True,
|
||||||
|
deprecated_reason="This option has been replaced by "
|
||||||
|
"'network_plugin_ipv4_enabled' and "
|
||||||
|
"'network_plugin_ipv6_enabled' options.",
|
||||||
deprecated_group='DEFAULT'),
|
deprecated_group='DEFAULT'),
|
||||||
cfg.IntOpt(
|
cfg.IntOpt(
|
||||||
'standalone_network_plugin_mtu',
|
'standalone_network_plugin_mtu',
|
||||||
@ -89,8 +94,10 @@ class StandaloneNetworkPlugin(network.NetworkBaseAPI):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, config_group_name=None, db_driver=None, label='user'):
|
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'
|
self.config_group_name = config_group_name or 'DEFAULT'
|
||||||
|
super(StandaloneNetworkPlugin,
|
||||||
|
self).__init__(config_group_name=self.config_group_name,
|
||||||
|
db_driver=db_driver)
|
||||||
CONF.register_opts(
|
CONF.register_opts(
|
||||||
standalone_network_plugin_opts,
|
standalone_network_plugin_opts,
|
||||||
group=self.config_group_name)
|
group=self.config_group_name)
|
||||||
@ -125,6 +132,34 @@ class StandaloneNetworkPlugin(network.NetworkBaseAPI):
|
|||||||
|
|
||||||
def _set_persistent_network_data(self):
|
def _set_persistent_network_data(self):
|
||||||
"""Sets persistent data for whole plugin."""
|
"""Sets persistent data for whole plugin."""
|
||||||
|
# NOTE(tommylikehu): Standalone plugin could only support
|
||||||
|
# either IPv4 or IPv6, so if both network_plugin_ipv4_enabled
|
||||||
|
# and network_plugin_ipv6_enabled are configured True
|
||||||
|
# we would only support IPv6.
|
||||||
|
ipv4_enabled = getattr(self.configuration,
|
||||||
|
'network_plugin_ipv4_enabled', None)
|
||||||
|
ipv6_enabled = getattr(self.configuration,
|
||||||
|
'network_plugin_ipv6_enabled', None)
|
||||||
|
|
||||||
|
if ipv4_enabled:
|
||||||
|
ip_version = 4
|
||||||
|
if ipv6_enabled:
|
||||||
|
ip_version = 6
|
||||||
|
if ipv4_enabled and ipv6_enabled:
|
||||||
|
LOG.warning("Only IPv6 is enabled, although both "
|
||||||
|
"'network_plugin_ipv4_enabled' and "
|
||||||
|
"'network_plugin_ipv6_enabled' are "
|
||||||
|
"configured True.")
|
||||||
|
|
||||||
|
if not (ipv4_enabled or ipv6_enabled):
|
||||||
|
ip_version = int(
|
||||||
|
self.configuration.standalone_network_plugin_ip_version)
|
||||||
|
LOG.warning("You're using a deprecated option that may"
|
||||||
|
" be removed and silently ignored in the future. "
|
||||||
|
"Please use 'network_plugin_ipv4_enabled' or "
|
||||||
|
"'network_plugin_ipv6_enabled' instead of "
|
||||||
|
"'standalone_network_plugin_ip_version'.")
|
||||||
|
|
||||||
self.network_type = (
|
self.network_type = (
|
||||||
self.configuration.standalone_network_plugin_network_type)
|
self.configuration.standalone_network_plugin_network_type)
|
||||||
self.segmentation_id = (
|
self.segmentation_id = (
|
||||||
@ -133,8 +168,7 @@ class StandaloneNetworkPlugin(network.NetworkBaseAPI):
|
|||||||
self.mask = self.configuration.standalone_network_plugin_mask
|
self.mask = self.configuration.standalone_network_plugin_mask
|
||||||
self.allowed_ip_ranges = (
|
self.allowed_ip_ranges = (
|
||||||
self.configuration.standalone_network_plugin_allowed_ip_ranges)
|
self.configuration.standalone_network_plugin_allowed_ip_ranges)
|
||||||
self.ip_version = int(
|
self.ip_version = ip_version
|
||||||
self.configuration.standalone_network_plugin_ip_version)
|
|
||||||
self.net = self._get_network()
|
self.net = self._get_network()
|
||||||
self.allowed_cidrs = self._get_list_of_allowed_addresses()
|
self.allowed_cidrs = self._get_list_of_allowed_addresses()
|
||||||
self.reserved_addresses = (
|
self.reserved_addresses = (
|
||||||
@ -191,7 +225,7 @@ class StandaloneNetworkPlugin(network.NetworkBaseAPI):
|
|||||||
msg = _("Config option "
|
msg = _("Config option "
|
||||||
"'standalone_network_plugin_allowed_ip_ranges' "
|
"'standalone_network_plugin_allowed_ip_ranges' "
|
||||||
"has incorrect value "
|
"has incorrect value "
|
||||||
"'%s'") % self.allowed_ip_ranges
|
"'%s'.") % self.allowed_ip_ranges
|
||||||
raise exception.NetworkBadConfigurationException(
|
raise exception.NetworkBadConfigurationException(
|
||||||
reason=msg)
|
reason=msg)
|
||||||
|
|
||||||
|
@ -143,6 +143,8 @@ class HostState(object):
|
|||||||
self.compression = False
|
self.compression = False
|
||||||
self.replication_type = None
|
self.replication_type = None
|
||||||
self.replication_domain = None
|
self.replication_domain = None
|
||||||
|
self.ipv4_support = None
|
||||||
|
self.ipv6_support = None
|
||||||
|
|
||||||
# PoolState for all pools
|
# PoolState for all pools
|
||||||
self.pools = {}
|
self.pools = {}
|
||||||
@ -332,6 +334,12 @@ class HostState(object):
|
|||||||
pool_cap['sg_consistent_snapshot_support'] = (
|
pool_cap['sg_consistent_snapshot_support'] = (
|
||||||
self.sg_consistent_snapshot_support)
|
self.sg_consistent_snapshot_support)
|
||||||
|
|
||||||
|
if self.ipv4_support is not None:
|
||||||
|
pool_cap['ipv4_support'] = self.ipv4_support
|
||||||
|
|
||||||
|
if self.ipv6_support is not None:
|
||||||
|
pool_cap['ipv6_support'] = self.ipv6_support
|
||||||
|
|
||||||
def update_backend(self, capability):
|
def update_backend(self, capability):
|
||||||
self.share_backend_name = capability.get('share_backend_name')
|
self.share_backend_name = capability.get('share_backend_name')
|
||||||
self.vendor_name = capability.get('vendor_name')
|
self.vendor_name = capability.get('vendor_name')
|
||||||
@ -351,6 +359,10 @@ class HostState(object):
|
|||||||
self.replication_domain = capability.get('replication_domain')
|
self.replication_domain = capability.get('replication_domain')
|
||||||
self.sg_consistent_snapshot_support = capability.get(
|
self.sg_consistent_snapshot_support = capability.get(
|
||||||
'share_group_stats', {}).get('consistent_snapshot_support')
|
'share_group_stats', {}).get('consistent_snapshot_support')
|
||||||
|
if capability.get('ipv4_support') is not None:
|
||||||
|
self.ipv4_support = capability['ipv4_support']
|
||||||
|
if capability.get('ipv6_support') is not None:
|
||||||
|
self.ipv6_support = capability['ipv6_support']
|
||||||
|
|
||||||
def consume_from_share(self, share):
|
def consume_from_share(self, share):
|
||||||
"""Incrementally update host state from an share."""
|
"""Incrementally update host state from an share."""
|
||||||
|
@ -55,6 +55,8 @@ def generate_stats(host_state, properties):
|
|||||||
host_state.max_over_subscription_ratio,
|
host_state.max_over_subscription_ratio,
|
||||||
'sg_consistent_snapshot_support': (
|
'sg_consistent_snapshot_support': (
|
||||||
host_state.sg_consistent_snapshot_support),
|
host_state.sg_consistent_snapshot_support),
|
||||||
|
'ipv4_support': host_state.ipv4_support,
|
||||||
|
'ipv6_support': host_state.ipv6_support,
|
||||||
}
|
}
|
||||||
|
|
||||||
host_caps = host_state.capabilities
|
host_caps = host_state.capabilities
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
|
import ipaddress
|
||||||
|
|
||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
|
|
||||||
@ -21,6 +22,8 @@ from manila.common import constants
|
|||||||
from manila.i18n import _
|
from manila.i18n import _
|
||||||
from manila import utils
|
from manila import utils
|
||||||
|
|
||||||
|
import six
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
LOG = log.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@ -459,6 +462,18 @@ class ShareInstanceAccess(ShareInstanceAccessDatabaseMixin):
|
|||||||
|
|
||||||
return access_rules_to_be_on_share
|
return access_rules_to_be_on_share
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _filter_ipv6_rules(rules, share_instance_proto):
|
||||||
|
filtered = []
|
||||||
|
for rule in rules:
|
||||||
|
if rule['access_type'] == 'ip' and share_instance_proto == 'nfs':
|
||||||
|
ip_version = ipaddress.ip_network(
|
||||||
|
six.text_type(rule['access_to'])).version
|
||||||
|
if 6 == ip_version:
|
||||||
|
continue
|
||||||
|
filtered.append(rule)
|
||||||
|
return filtered
|
||||||
|
|
||||||
def _get_rules_to_send_to_driver(self, context, share_instance):
|
def _get_rules_to_send_to_driver(self, context, share_instance):
|
||||||
add_rules = []
|
add_rules = []
|
||||||
delete_rules = []
|
delete_rules = []
|
||||||
@ -472,6 +487,7 @@ class ShareInstanceAccess(ShareInstanceAccessDatabaseMixin):
|
|||||||
share_instance_id=share_instance['id'])
|
share_instance_id=share_instance['id'])
|
||||||
# Update queued rules to transitional states
|
# Update queued rules to transitional states
|
||||||
for rule in existing_rules_in_db:
|
for rule in existing_rules_in_db:
|
||||||
|
|
||||||
if rule['state'] == constants.ACCESS_STATE_APPLYING:
|
if rule['state'] == constants.ACCESS_STATE_APPLYING:
|
||||||
add_rules.append(rule)
|
add_rules.append(rule)
|
||||||
elif rule['state'] == constants.ACCESS_STATE_DENYING:
|
elif rule['state'] == constants.ACCESS_STATE_DENYING:
|
||||||
@ -480,6 +496,13 @@ class ShareInstanceAccess(ShareInstanceAccessDatabaseMixin):
|
|||||||
access_rules_to_be_on_share = [
|
access_rules_to_be_on_share = [
|
||||||
r for r in existing_rules_in_db if r['id'] not in delete_rule_ids
|
r for r in existing_rules_in_db if r['id'] not in delete_rule_ids
|
||||||
]
|
]
|
||||||
|
share = self.db.share_get(context, share_instance['share_id'])
|
||||||
|
si_proto = share['share_proto'].lower()
|
||||||
|
if not self.driver.ipv6_implemented:
|
||||||
|
add_rules = self._filter_ipv6_rules(add_rules, si_proto)
|
||||||
|
delete_rules = self._filter_ipv6_rules(delete_rules, si_proto)
|
||||||
|
access_rules_to_be_on_share = self._filter_ipv6_rules(
|
||||||
|
access_rules_to_be_on_share, si_proto)
|
||||||
return access_rules_to_be_on_share, add_rules, delete_rules
|
return access_rules_to_be_on_share, add_rules, delete_rules
|
||||||
|
|
||||||
def _check_needs_refresh(self, context, share_instance_id):
|
def _check_needs_refresh(self, context, share_instance_id):
|
||||||
|
@ -252,6 +252,8 @@ class ShareDriver(object):
|
|||||||
self.configuration = kwargs.get('configuration', None)
|
self.configuration = kwargs.get('configuration', None)
|
||||||
self.initialized = False
|
self.initialized = False
|
||||||
self._stats = {}
|
self._stats = {}
|
||||||
|
self.ip_version = None
|
||||||
|
self.ipv6_implemented = False
|
||||||
|
|
||||||
self.pools = []
|
self.pools = []
|
||||||
if self.configuration:
|
if self.configuration:
|
||||||
@ -1116,6 +1118,7 @@ class ShareDriver(object):
|
|||||||
replication_domain=self.replication_domain,
|
replication_domain=self.replication_domain,
|
||||||
filter_function=self.get_filter_function(),
|
filter_function=self.get_filter_function(),
|
||||||
goodness_function=self.get_goodness_function(),
|
goodness_function=self.get_goodness_function(),
|
||||||
|
ipv4_support=True,
|
||||||
)
|
)
|
||||||
if isinstance(data, dict):
|
if isinstance(data, dict):
|
||||||
common.update(data)
|
common.update(data)
|
||||||
@ -1126,6 +1129,7 @@ class ShareDriver(object):
|
|||||||
'consistent_snapshot_support'),
|
'consistent_snapshot_support'),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.add_ip_version_capability(common)
|
||||||
self._stats = common
|
self._stats = common
|
||||||
|
|
||||||
def get_share_server_pools(self, share_server):
|
def get_share_server_pools(self, share_server):
|
||||||
@ -2446,3 +2450,61 @@ class ShareDriver(object):
|
|||||||
LOG.debug("This backend does not support gathering 'used_size' of "
|
LOG.debug("This backend does not support gathering 'used_size' of "
|
||||||
"shares created on it.")
|
"shares created on it.")
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
def get_configured_ip_version(self):
|
||||||
|
""""Get Configured IP versions when DHSS is false.
|
||||||
|
|
||||||
|
The supported versions are returned with list, possible
|
||||||
|
values are: [4], [6] or [4, 6]
|
||||||
|
Each driver could override the method to return the IP version
|
||||||
|
which represents its self configuration.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# For drivers that haven't implemented IPv6, assume legacy behavior
|
||||||
|
if not self.ipv6_implemented:
|
||||||
|
return [4]
|
||||||
|
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def add_ip_version_capability(self, data):
|
||||||
|
"""Add IP version support capabilities.
|
||||||
|
|
||||||
|
When DHSS is true, the capabilities are determined by driver
|
||||||
|
and configured network plugin.
|
||||||
|
When DHSS is false, the capabilities are determined by driver and its
|
||||||
|
configuration.
|
||||||
|
:param data: the capability dictionary
|
||||||
|
:returns: capability data
|
||||||
|
"""
|
||||||
|
ipv4_support = data.get('ipv4_support', False)
|
||||||
|
ipv6_support = data.get('ipv6_support', False)
|
||||||
|
if self.ip_version is None:
|
||||||
|
if self.driver_handles_share_servers:
|
||||||
|
user_network_version = self.network_api.enabled_ip_version
|
||||||
|
if self.admin_network_api:
|
||||||
|
if (user_network_version ==
|
||||||
|
self.admin_network_api.enabled_ip_version):
|
||||||
|
self.ip_version = user_network_version
|
||||||
|
else:
|
||||||
|
LOG.warning("The enabled IP version for the admin "
|
||||||
|
"network plugin is different from "
|
||||||
|
"that of user network plugin, this "
|
||||||
|
"may lead to the backend never being "
|
||||||
|
"chosen by the scheduler when ip "
|
||||||
|
"version is specified in the share "
|
||||||
|
"type.")
|
||||||
|
else:
|
||||||
|
self.ip_version = user_network_version
|
||||||
|
else:
|
||||||
|
self.ip_version = self.get_configured_ip_version()
|
||||||
|
|
||||||
|
if not isinstance(self.ip_version, list):
|
||||||
|
self.ip_version = [self.ip_version]
|
||||||
|
|
||||||
|
data['ipv4_support'] = (4 in self.ip_version) and ipv4_support
|
||||||
|
data['ipv6_support'] = (6 in self.ip_version) and ipv6_support
|
||||||
|
if not (data['ipv4_support'] or data['ipv6_support']):
|
||||||
|
LOG.error("Backend %s capabilities 'ipv4_support' "
|
||||||
|
"and 'ipv6_support' are both False.",
|
||||||
|
data['share_backend_name'])
|
||||||
|
return data
|
||||||
|
@ -13,6 +13,8 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import copy
|
||||||
|
import netaddr
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
|
||||||
@ -183,7 +185,20 @@ class NFSHelper(NASHelperBase):
|
|||||||
|
|
||||||
def create_exports(self, server, share_name, recreate=False):
|
def create_exports(self, server, share_name, recreate=False):
|
||||||
path = os.path.join(self.configuration.share_mount_path, share_name)
|
path = os.path.join(self.configuration.share_mount_path, share_name)
|
||||||
return self.get_exports_for_share(server, path)
|
server_copy = copy.copy(server)
|
||||||
|
public_addresses = []
|
||||||
|
if 'public_addresses' in server_copy:
|
||||||
|
for address in server_copy['public_addresses']:
|
||||||
|
public_addresses.append(
|
||||||
|
self._get_parsed_address_or_cidr(address))
|
||||||
|
server_copy['public_addresses'] = public_addresses
|
||||||
|
|
||||||
|
for t in ['public_address', 'admin_ip', 'ip']:
|
||||||
|
address = server_copy.get(t)
|
||||||
|
if address is not None:
|
||||||
|
server_copy[t] = self._get_parsed_address_or_cidr(address)
|
||||||
|
|
||||||
|
return self.get_exports_for_share(server_copy, path)
|
||||||
|
|
||||||
def init_helper(self, server):
|
def init_helper(self, server):
|
||||||
try:
|
try:
|
||||||
@ -198,12 +213,6 @@ class NFSHelper(NASHelperBase):
|
|||||||
def remove_exports(self, server, share_name):
|
def remove_exports(self, server, share_name):
|
||||||
"""Remove exports."""
|
"""Remove exports."""
|
||||||
|
|
||||||
def _get_parsed_access_to(self, access_to):
|
|
||||||
netmask = utils.cidr_to_netmask(access_to)
|
|
||||||
if netmask == '255.255.255.255':
|
|
||||||
return access_to.split('/')[0]
|
|
||||||
return access_to.split('/')[0] + '/' + netmask
|
|
||||||
|
|
||||||
@nfs_synchronized
|
@nfs_synchronized
|
||||||
def update_access(self, server, share_name, access_rules, add_rules,
|
def update_access(self, server, share_name, access_rules, add_rules,
|
||||||
delete_rules):
|
delete_rules):
|
||||||
@ -234,8 +243,9 @@ class NFSHelper(NASHelperBase):
|
|||||||
server,
|
server,
|
||||||
['sudo', 'exportfs', '-o',
|
['sudo', 'exportfs', '-o',
|
||||||
rules_options % access['access_level'],
|
rules_options % access['access_level'],
|
||||||
':'.join((self._get_parsed_access_to(access['access_to']),
|
':'.join((
|
||||||
local_path))])
|
self._get_parsed_address_or_cidr(access['access_to']),
|
||||||
|
local_path))])
|
||||||
self._sync_nfs_temp_and_perm_files(server)
|
self._sync_nfs_temp_and_perm_files(server)
|
||||||
# Adding/Deleting specific rules
|
# Adding/Deleting specific rules
|
||||||
else:
|
else:
|
||||||
@ -245,7 +255,7 @@ class NFSHelper(NASHelperBase):
|
|||||||
(const.ACCESS_LEVEL_RO, const.ACCESS_LEVEL_RW))
|
(const.ACCESS_LEVEL_RO, const.ACCESS_LEVEL_RW))
|
||||||
|
|
||||||
for access in delete_rules:
|
for access in delete_rules:
|
||||||
access['access_to'] = self._get_parsed_access_to(
|
access['access_to'] = self._get_parsed_address_or_cidr(
|
||||||
access['access_to'])
|
access['access_to'])
|
||||||
try:
|
try:
|
||||||
self.validate_access_rules(
|
self.validate_access_rules(
|
||||||
@ -265,7 +275,7 @@ class NFSHelper(NASHelperBase):
|
|||||||
if delete_rules:
|
if delete_rules:
|
||||||
self._sync_nfs_temp_and_perm_files(server)
|
self._sync_nfs_temp_and_perm_files(server)
|
||||||
for access in add_rules:
|
for access in add_rules:
|
||||||
access['access_to'] = self._get_parsed_access_to(
|
access['access_to'] = self._get_parsed_address_or_cidr(
|
||||||
access['access_to'])
|
access['access_to'])
|
||||||
found_item = re.search(
|
found_item = re.search(
|
||||||
re.escape(local_path) + '[\s\n]*' + re.escape(
|
re.escape(local_path) + '[\s\n]*' + re.escape(
|
||||||
@ -290,6 +300,22 @@ class NFSHelper(NASHelperBase):
|
|||||||
if add_rules:
|
if add_rules:
|
||||||
self._sync_nfs_temp_and_perm_files(server)
|
self._sync_nfs_temp_and_perm_files(server)
|
||||||
|
|
||||||
|
def _get_parsed_address_or_cidr(self, access_to):
|
||||||
|
try:
|
||||||
|
network = netaddr.IPNetwork(access_to)
|
||||||
|
except netaddr.AddrFormatError:
|
||||||
|
raise exception.InvalidInput(
|
||||||
|
reason=_("Invalid address or cidr supplied %s.") % access_to)
|
||||||
|
mask_length = network.netmask.netmask_bits()
|
||||||
|
address = access_to.split('/')[0]
|
||||||
|
if network.version == 4:
|
||||||
|
if mask_length == 32:
|
||||||
|
return address
|
||||||
|
return '%s/%s' % (address, mask_length)
|
||||||
|
if mask_length == 128:
|
||||||
|
return "[%s]" % address
|
||||||
|
return "[%s]/%s" % (address, mask_length)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_host_list(output, local_path):
|
def get_host_list(output, local_path):
|
||||||
entries = []
|
entries = []
|
||||||
|
@ -18,6 +18,7 @@ LVM Driver for shares.
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import ipaddress
|
||||||
import math
|
import math
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
@ -154,6 +155,7 @@ class LVMShareDriver(LVMMixin, driver.ShareDriver):
|
|||||||
self.configuration.share_mount_path = (
|
self.configuration.share_mount_path = (
|
||||||
self.configuration.lvm_share_export_root)
|
self.configuration.lvm_share_export_root)
|
||||||
self._helpers = None
|
self._helpers = None
|
||||||
|
self.configured_ip_version = None
|
||||||
self.backend_name = self.configuration.safe_get(
|
self.backend_name = self.configuration.safe_get(
|
||||||
'share_backend_name') or 'LVM'
|
'share_backend_name') or 'LVM'
|
||||||
# Set of parameters used for compatibility with
|
# Set of parameters used for compatibility with
|
||||||
@ -168,6 +170,7 @@ class LVMShareDriver(LVMMixin, driver.ShareDriver):
|
|||||||
else:
|
else:
|
||||||
self.share_server['public_addresses'] = (
|
self.share_server['public_addresses'] = (
|
||||||
self.configuration.lvm_share_export_ips)
|
self.configuration.lvm_share_export_ips)
|
||||||
|
self.ipv6_implemented = True
|
||||||
|
|
||||||
def _ssh_exec_as_root(self, server, command, check_exit_code=True):
|
def _ssh_exec_as_root(self, server, command, check_exit_code=True):
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
@ -212,7 +215,8 @@ class LVMShareDriver(LVMMixin, driver.ShareDriver):
|
|||||||
'revert_to_snapshot_support': True,
|
'revert_to_snapshot_support': True,
|
||||||
'mount_snapshot_support': True,
|
'mount_snapshot_support': True,
|
||||||
'driver_name': 'LVMShareDriver',
|
'driver_name': 'LVMShareDriver',
|
||||||
'pools': self.get_share_server_pools()
|
'pools': self.get_share_server_pools(),
|
||||||
|
'ipv6_support': True
|
||||||
}
|
}
|
||||||
super(LVMShareDriver, self)._update_share_stats(data)
|
super(LVMShareDriver, self)._update_share_stats(data)
|
||||||
|
|
||||||
@ -431,6 +435,31 @@ class LVMShareDriver(LVMMixin, driver.ShareDriver):
|
|||||||
super(LVMShareDriver, self).delete_snapshot(context, snapshot,
|
super(LVMShareDriver, self).delete_snapshot(context, snapshot,
|
||||||
share_server)
|
share_server)
|
||||||
|
|
||||||
|
def get_configured_ip_version(self):
|
||||||
|
""""Get Configured IP versions when DHSS is false."""
|
||||||
|
if self.configured_ip_version is None:
|
||||||
|
try:
|
||||||
|
self.configured_ip_version = []
|
||||||
|
if self.configuration.lvm_share_export_ip:
|
||||||
|
self.configured_ip_version.append(ipaddress.ip_address(
|
||||||
|
six.text_type(
|
||||||
|
self.configuration.lvm_share_export_ip)).version)
|
||||||
|
else:
|
||||||
|
for ip in self.configuration.lvm_share_export_ips:
|
||||||
|
self.configured_ip_version.append(
|
||||||
|
ipaddress.ip_address(six.text_type(ip)).version)
|
||||||
|
except Exception:
|
||||||
|
if self.configuration.lvm_share_export_ip:
|
||||||
|
message = (_("Invalid 'lvm_share_export_ip' option "
|
||||||
|
"supplied %s.") %
|
||||||
|
self.configuration.lvm_share_export_ip)
|
||||||
|
else:
|
||||||
|
message = (_("Invalid 'lvm_share_export_ips' option "
|
||||||
|
"supplied %s.") %
|
||||||
|
self.configuration.lvm_share_export_ips)
|
||||||
|
raise exception.InvalidInput(reason=message)
|
||||||
|
return self.configured_ip_version
|
||||||
|
|
||||||
def snapshot_update_access(self, context, snapshot, access_rules,
|
def snapshot_update_access(self, context, snapshot, access_rules,
|
||||||
add_rules, delete_rules, share_server=None):
|
add_rules, delete_rules, share_server=None):
|
||||||
"""Update access rules for given snapshot.
|
"""Update access rules for given snapshot.
|
||||||
|
@ -121,13 +121,13 @@ no_share_servers_handling_mode_opts = [
|
|||||||
"service_net_name_or_ip",
|
"service_net_name_or_ip",
|
||||||
help="Can be either name of network that is used by service "
|
help="Can be either name of network that is used by service "
|
||||||
"instance within Nova to get IP address or IP address itself "
|
"instance within Nova to get IP address or IP address itself "
|
||||||
"for managing shares there. "
|
"(either IPv4 or IPv6) for managing shares there. "
|
||||||
"Used only when share servers handling is disabled."),
|
"Used only when share servers handling is disabled."),
|
||||||
cfg.HostAddressOpt(
|
cfg.HostAddressOpt(
|
||||||
"tenant_net_name_or_ip",
|
"tenant_net_name_or_ip",
|
||||||
help="Can be either name of network that is used by service "
|
help="Can be either name of network that is used by service "
|
||||||
"instance within Nova to get IP address or IP address itself "
|
"instance within Nova to get IP address or IP address itself "
|
||||||
"for exporting shares. "
|
"(either IPv4 or IPv6) for exporting shares. "
|
||||||
"Used only when share servers handling is disabled."),
|
"Used only when share servers handling is disabled."),
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -241,13 +241,13 @@ class ServiceInstanceManager(object):
|
|||||||
self.admin_context,
|
self.admin_context,
|
||||||
self.get_config_option('service_instance_name_or_id'))
|
self.get_config_option('service_instance_name_or_id'))
|
||||||
|
|
||||||
if netutils.is_valid_ipv4(data['service_net_name_or_ip']):
|
if netutils.is_valid_ip(data['service_net_name_or_ip']):
|
||||||
data['private_address'] = [data['service_net_name_or_ip']]
|
data['private_address'] = [data['service_net_name_or_ip']]
|
||||||
else:
|
else:
|
||||||
data['private_address'] = self._get_addresses_by_network_name(
|
data['private_address'] = self._get_addresses_by_network_name(
|
||||||
data['service_net_name_or_ip'], data['instance'])
|
data['service_net_name_or_ip'], data['instance'])
|
||||||
|
|
||||||
if netutils.is_valid_ipv4(data['tenant_net_name_or_ip']):
|
if netutils.is_valid_ip(data['tenant_net_name_or_ip']):
|
||||||
data['public_address'] = [data['tenant_net_name_or_ip']]
|
data['public_address'] = [data['tenant_net_name_or_ip']]
|
||||||
else:
|
else:
|
||||||
data['public_address'] = self._get_addresses_by_network_name(
|
data['public_address'] = self._get_addresses_by_network_name(
|
||||||
@ -267,13 +267,13 @@ class ServiceInstanceManager(object):
|
|||||||
'instance_id': data['instance']['id'],
|
'instance_id': data['instance']['id'],
|
||||||
}
|
}
|
||||||
for key in ('private_address', 'public_address'):
|
for key in ('private_address', 'public_address'):
|
||||||
data[key + '_v4'] = None
|
data[key + '_first'] = None
|
||||||
for address in data[key]:
|
for address in data[key]:
|
||||||
if netutils.is_valid_ipv4(address):
|
if netutils.is_valid_ip(address):
|
||||||
data[key + '_v4'] = address
|
data[key + '_first'] = address
|
||||||
break
|
break
|
||||||
share_server['ip'] = data['private_address_v4']
|
share_server['ip'] = data['private_address_first']
|
||||||
share_server['public_address'] = data['public_address_v4']
|
share_server['public_address'] = data['public_address_first']
|
||||||
return {'backend_details': share_server}
|
return {'backend_details': share_server}
|
||||||
|
|
||||||
def _get_addresses_by_network_name(self, net_name, server):
|
def _get_addresses_by_network_name(self, net_name, server):
|
||||||
|
@ -44,7 +44,7 @@ fake_neutron_port = {
|
|||||||
"binding:capabilities": {"port_filter": True},
|
"binding:capabilities": {"port_filter": True},
|
||||||
"mac_address": "test_mac",
|
"mac_address": "test_mac",
|
||||||
"fixed_ips": [
|
"fixed_ips": [
|
||||||
{"subnet_id": "test_subnet_id", "ip_address": "test_ip"},
|
{"subnet_id": "test_subnet_id", "ip_address": "203.0.113.100"},
|
||||||
],
|
],
|
||||||
"id": "test_port_id",
|
"id": "test_port_id",
|
||||||
"security_groups": ["fake_sec_group_id"],
|
"security_groups": ["fake_sec_group_id"],
|
||||||
@ -1505,6 +1505,7 @@ class NeutronBindNetworkPluginWithNormalTypeTest(test.TestCase):
|
|||||||
[fake_neutron_port], fake_share_server)
|
[fake_neutron_port], fake_share_server)
|
||||||
|
|
||||||
|
|
||||||
|
@ddt.ddt
|
||||||
class NeutronBindSingleNetworkPluginWithNormalTypeTest(test.TestCase):
|
class NeutronBindSingleNetworkPluginWithNormalTypeTest(test.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(NeutronBindSingleNetworkPluginWithNormalTypeTest, self).setUp()
|
super(NeutronBindSingleNetworkPluginWithNormalTypeTest, self).setUp()
|
||||||
@ -1588,3 +1589,40 @@ class NeutronBindSingleNetworkPluginWithNormalTypeTest(test.TestCase):
|
|||||||
|
|
||||||
self.bind_plugin._wait_for_ports_bind.assert_called_once_with(
|
self.bind_plugin._wait_for_ports_bind.assert_called_once_with(
|
||||||
[fake_neutron_port], fake_share_server)
|
[fake_neutron_port], fake_share_server)
|
||||||
|
|
||||||
|
@ddt.data({'fix_ips': [{'ip_address': 'test_ip'},
|
||||||
|
{'ip_address': '10.78.223.129'}],
|
||||||
|
'ip_version': 4},
|
||||||
|
{'fix_ips': [{'ip_address': 'test_ip'},
|
||||||
|
{'ip_address': 'ad80::abaa:0:c2:2'}],
|
||||||
|
'ip_version': 6},
|
||||||
|
{'fix_ips': [{'ip_address': '10.78.223.129'},
|
||||||
|
{'ip_address': 'ad80::abaa:0:c2:2'}],
|
||||||
|
'ip_version': 6},
|
||||||
|
)
|
||||||
|
@ddt.unpack
|
||||||
|
def test__get_matched_ip_address(self, fix_ips, ip_version):
|
||||||
|
result = self.bind_plugin._get_matched_ip_address(fix_ips, ip_version)
|
||||||
|
self.assertEqual(fix_ips[1]['ip_address'], result)
|
||||||
|
|
||||||
|
@ddt.data({'fix_ips': [{'ip_address': 'test_ip_1'},
|
||||||
|
{'ip_address': 'test_ip_2'}],
|
||||||
|
'ip_version': (4, 6)},
|
||||||
|
{'fix_ips': [{'ip_address': 'ad80::abaa:0:c2:1'},
|
||||||
|
{'ip_address': 'ad80::abaa:0:c2:2'}],
|
||||||
|
'ip_version': (4, )},
|
||||||
|
{'fix_ips': [{'ip_address': '192.0.0.2'},
|
||||||
|
{'ip_address': '192.0.0.3'}],
|
||||||
|
'ip_version': (6, )},
|
||||||
|
{'fix_ips': [{'ip_address': '192.0.0.2/12'},
|
||||||
|
{'ip_address': '192.0.0.330'},
|
||||||
|
{'ip_address': 'ad80::001::ad80'},
|
||||||
|
{'ip_address': 'ad80::abaa:0:c2:2/64'}],
|
||||||
|
'ip_version': (4, 6)},
|
||||||
|
)
|
||||||
|
@ddt.unpack
|
||||||
|
def test__get_matched_ip_address_illegal(self, fix_ips, ip_version):
|
||||||
|
for version in ip_version:
|
||||||
|
self.assertRaises(exception.NetworkBadConfigurationException,
|
||||||
|
self.bind_plugin._get_matched_ip_address,
|
||||||
|
fix_ips, version)
|
||||||
|
@ -70,7 +70,7 @@ class StandaloneNetworkPluginTest(test.TestCase):
|
|||||||
'standalone_network_plugin_segmentation_id': 1001,
|
'standalone_network_plugin_segmentation_id': 1001,
|
||||||
'standalone_network_plugin_allowed_ip_ranges': (
|
'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'),
|
'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,
|
'network_plugin_ipv4_enabled': True,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
allowed_cidrs = [
|
allowed_cidrs = [
|
||||||
@ -104,7 +104,7 @@ class StandaloneNetworkPluginTest(test.TestCase):
|
|||||||
'standalone_network_plugin_gateway': (
|
'standalone_network_plugin_gateway': (
|
||||||
'2001:cdba::3257:9652'),
|
'2001:cdba::3257:9652'),
|
||||||
'standalone_network_plugin_mask': '48',
|
'standalone_network_plugin_mask': '48',
|
||||||
'standalone_network_plugin_ip_version': 6,
|
'network_plugin_ipv6_enabled': True,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
with test_utils.create_temp_config_with_opts(data):
|
with test_utils.create_temp_config_with_opts(data):
|
||||||
@ -138,7 +138,7 @@ class StandaloneNetworkPluginTest(test.TestCase):
|
|||||||
'standalone_network_plugin_segmentation_id': 3999,
|
'standalone_network_plugin_segmentation_id': 3999,
|
||||||
'standalone_network_plugin_allowed_ip_ranges': (
|
'standalone_network_plugin_allowed_ip_ranges': (
|
||||||
'2001:db8::-2001:db8:0000:0000:0000:007f:ffff:ffff'),
|
'2001:db8::-2001:db8:0000:0000:0000:007f:ffff:ffff'),
|
||||||
'standalone_network_plugin_ip_version': 6,
|
'network_plugin_ipv6_enabled': True,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
with test_utils.create_temp_config_with_opts(data):
|
with test_utils.create_temp_config_with_opts(data):
|
||||||
@ -168,7 +168,7 @@ class StandaloneNetworkPluginTest(test.TestCase):
|
|||||||
'standalone_network_plugin_mask': '255.255.0.0',
|
'standalone_network_plugin_mask': '255.255.0.0',
|
||||||
'standalone_network_plugin_network_type': network_type,
|
'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,
|
'network_plugin_ipv4_enabled': True,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
with test_utils.create_temp_config_with_opts(data):
|
with test_utils.create_temp_config_with_opts(data):
|
||||||
@ -186,7 +186,7 @@ class StandaloneNetworkPluginTest(test.TestCase):
|
|||||||
'standalone_network_plugin_mask': '255.255.0.0',
|
'standalone_network_plugin_mask': '255.255.0.0',
|
||||||
'standalone_network_plugin_network_type': fake_network_type,
|
'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,
|
'network_plugin_ipv4_enabled': True,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
with test_utils.create_temp_config_with_opts(data):
|
with test_utils.create_temp_config_with_opts(data):
|
||||||
@ -255,10 +255,15 @@ class StandaloneNetworkPluginTest(test.TestCase):
|
|||||||
data = {
|
data = {
|
||||||
group_name: {
|
group_name: {
|
||||||
'standalone_network_plugin_gateway': gateway,
|
'standalone_network_plugin_gateway': gateway,
|
||||||
'standalone_network_plugin_ip_version': vers,
|
|
||||||
'standalone_network_plugin_mask': '25',
|
'standalone_network_plugin_mask': '25',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
if vers == 4:
|
||||||
|
data[group_name]['network_plugin_ipv4_enabled'] = True
|
||||||
|
if vers == 6:
|
||||||
|
data[group_name]['network_plugin_ipv4_enabled'] = False
|
||||||
|
data[group_name]['network_plugin_ipv6_enabled'] = True
|
||||||
|
|
||||||
with test_utils.create_temp_config_with_opts(data):
|
with test_utils.create_temp_config_with_opts(data):
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
exception.NetworkBadConfigurationException,
|
exception.NetworkBadConfigurationException,
|
||||||
@ -319,7 +324,7 @@ class StandaloneNetworkPluginTest(test.TestCase):
|
|||||||
'DEFAULT': {
|
'DEFAULT': {
|
||||||
'standalone_network_plugin_gateway': '2001:db8::0001',
|
'standalone_network_plugin_gateway': '2001:db8::0001',
|
||||||
'standalone_network_plugin_mask': '64',
|
'standalone_network_plugin_mask': '64',
|
||||||
'standalone_network_plugin_ip_version': 6,
|
'network_plugin_ipv6_enabled': True,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
with test_utils.create_temp_config_with_opts(data):
|
with test_utils.create_temp_config_with_opts(data):
|
||||||
|
@ -637,7 +637,9 @@ class HostStateTestCase(test.TestCase):
|
|||||||
share_capability = {'total_capacity_gb': 0,
|
share_capability = {'total_capacity_gb': 0,
|
||||||
'free_capacity_gb': 100,
|
'free_capacity_gb': 100,
|
||||||
'reserved_percentage': 0,
|
'reserved_percentage': 0,
|
||||||
'timestamp': None}
|
'timestamp': None,
|
||||||
|
'ipv4_support': True,
|
||||||
|
'ipv6_support': False}
|
||||||
fake_host = host_manager.HostState('host1', share_capability)
|
fake_host = host_manager.HostState('host1', share_capability)
|
||||||
self.assertIsNone(fake_host.free_capacity_gb)
|
self.assertIsNone(fake_host.free_capacity_gb)
|
||||||
|
|
||||||
@ -646,9 +648,13 @@ class HostStateTestCase(test.TestCase):
|
|||||||
# Backend level stats remain uninitialized
|
# Backend level stats remain uninitialized
|
||||||
self.assertEqual(0, fake_host.total_capacity_gb)
|
self.assertEqual(0, fake_host.total_capacity_gb)
|
||||||
self.assertIsNone(fake_host.free_capacity_gb)
|
self.assertIsNone(fake_host.free_capacity_gb)
|
||||||
|
self.assertTrue(fake_host.ipv4_support)
|
||||||
|
self.assertFalse(fake_host.ipv6_support)
|
||||||
# Pool stats has been updated
|
# Pool stats has been updated
|
||||||
self.assertEqual(0, fake_host.pools['_pool0'].total_capacity_gb)
|
self.assertEqual(0, fake_host.pools['_pool0'].total_capacity_gb)
|
||||||
self.assertEqual(100, fake_host.pools['_pool0'].free_capacity_gb)
|
self.assertEqual(100, fake_host.pools['_pool0'].free_capacity_gb)
|
||||||
|
self.assertTrue(fake_host.pools['_pool0'].ipv4_support)
|
||||||
|
self.assertFalse(fake_host.pools['_pool0'].ipv6_support)
|
||||||
|
|
||||||
# Test update for existing host state
|
# Test update for existing host state
|
||||||
share_capability.update(dict(total_capacity_gb=1000))
|
share_capability.update(dict(total_capacity_gb=1000))
|
||||||
@ -674,6 +680,8 @@ class HostStateTestCase(test.TestCase):
|
|||||||
'vendor_name': 'OpenStack',
|
'vendor_name': 'OpenStack',
|
||||||
'driver_version': '1.1',
|
'driver_version': '1.1',
|
||||||
'storage_protocol': 'NFS_CIFS',
|
'storage_protocol': 'NFS_CIFS',
|
||||||
|
'ipv4_support': True,
|
||||||
|
'ipv6_support': False,
|
||||||
'pools': [
|
'pools': [
|
||||||
{'pool_name': 'pool1',
|
{'pool_name': 'pool1',
|
||||||
'total_capacity_gb': 500,
|
'total_capacity_gb': 500,
|
||||||
@ -707,6 +715,8 @@ class HostStateTestCase(test.TestCase):
|
|||||||
self.assertEqual('NFS_CIFS', fake_host.storage_protocol)
|
self.assertEqual('NFS_CIFS', fake_host.storage_protocol)
|
||||||
self.assertEqual('OpenStack', fake_host.vendor_name)
|
self.assertEqual('OpenStack', fake_host.vendor_name)
|
||||||
self.assertEqual('1.1', fake_host.driver_version)
|
self.assertEqual('1.1', fake_host.driver_version)
|
||||||
|
self.assertTrue(fake_host.ipv4_support)
|
||||||
|
self.assertFalse(fake_host.ipv6_support)
|
||||||
|
|
||||||
# Backend level stats remain uninitialized
|
# Backend level stats remain uninitialized
|
||||||
self.assertEqual(0, fake_host.total_capacity_gb)
|
self.assertEqual(0, fake_host.total_capacity_gb)
|
||||||
@ -716,8 +726,12 @@ class HostStateTestCase(test.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(500, fake_host.pools['pool1'].total_capacity_gb)
|
self.assertEqual(500, fake_host.pools['pool1'].total_capacity_gb)
|
||||||
self.assertEqual(230, fake_host.pools['pool1'].free_capacity_gb)
|
self.assertEqual(230, fake_host.pools['pool1'].free_capacity_gb)
|
||||||
|
self.assertTrue(fake_host.pools['pool1'].ipv4_support)
|
||||||
|
self.assertFalse(fake_host.pools['pool1'].ipv6_support)
|
||||||
self.assertEqual(1024, fake_host.pools['pool2'].total_capacity_gb)
|
self.assertEqual(1024, fake_host.pools['pool2'].total_capacity_gb)
|
||||||
self.assertEqual(1024, fake_host.pools['pool2'].free_capacity_gb)
|
self.assertEqual(1024, fake_host.pools['pool2'].free_capacity_gb)
|
||||||
|
self.assertTrue(fake_host.pools['pool2'].ipv4_support)
|
||||||
|
self.assertFalse(fake_host.pools['pool2'].ipv6_support)
|
||||||
|
|
||||||
capability = {
|
capability = {
|
||||||
'share_backend_name': 'Backend1',
|
'share_backend_name': 'Backend1',
|
||||||
@ -872,14 +886,17 @@ class PoolStateTestCase(test.TestCase):
|
|||||||
'share_capability':
|
'share_capability':
|
||||||
{'total_capacity_gb': 1024, 'free_capacity_gb': 512,
|
{'total_capacity_gb': 1024, 'free_capacity_gb': 512,
|
||||||
'reserved_percentage': 0, 'timestamp': None,
|
'reserved_percentage': 0, 'timestamp': None,
|
||||||
'cap1': 'val1', 'cap2': 'val2'},
|
'cap1': 'val1', 'cap2': 'val2', 'ipv4_support': True,
|
||||||
|
'ipv6_support': False},
|
||||||
'instances': []
|
'instances': []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'share_capability':
|
'share_capability':
|
||||||
{'total_capacity_gb': 1024, 'free_capacity_gb': 512,
|
{'total_capacity_gb': 1024, 'free_capacity_gb': 512,
|
||||||
'allocated_capacity_gb': 256, 'reserved_percentage': 0,
|
'allocated_capacity_gb': 256, 'reserved_percentage': 0,
|
||||||
'timestamp': None, 'cap1': 'val1', 'cap2': 'val2'},
|
'timestamp': None, 'cap1': 'val1', 'cap2': 'val2',
|
||||||
|
'ipv4_support': False, 'ipv6_support': True
|
||||||
|
},
|
||||||
'instances':
|
'instances':
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
@ -894,14 +911,17 @@ class PoolStateTestCase(test.TestCase):
|
|||||||
'share_capability':
|
'share_capability':
|
||||||
{'total_capacity_gb': 1024, 'free_capacity_gb': 512,
|
{'total_capacity_gb': 1024, 'free_capacity_gb': 512,
|
||||||
'allocated_capacity_gb': 256, 'reserved_percentage': 0,
|
'allocated_capacity_gb': 256, 'reserved_percentage': 0,
|
||||||
'timestamp': None, 'cap1': 'val1', 'cap2': 'val2'},
|
'timestamp': None, 'cap1': 'val1', 'cap2': 'val2',
|
||||||
|
'ipv4_support': True, 'ipv6_support': True},
|
||||||
'instances': []
|
'instances': []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'share_capability':
|
'share_capability':
|
||||||
{'total_capacity_gb': 1024, 'free_capacity_gb': 512,
|
{'total_capacity_gb': 1024, 'free_capacity_gb': 512,
|
||||||
'provisioned_capacity_gb': 256, 'reserved_percentage': 0,
|
'provisioned_capacity_gb': 256, 'reserved_percentage': 0,
|
||||||
'timestamp': None, 'cap1': 'val1', 'cap2': 'val2'},
|
'timestamp': None, 'cap1': 'val1', 'cap2': 'val2',
|
||||||
|
'ipv4_support': False, 'ipv6_support': False
|
||||||
|
},
|
||||||
'instances':
|
'instances':
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
@ -976,3 +996,9 @@ class PoolStateTestCase(test.TestCase):
|
|||||||
fake_pool.allocated_capacity_gb)
|
fake_pool.allocated_capacity_gb)
|
||||||
self.assertEqual(share_capability['provisioned_capacity_gb'],
|
self.assertEqual(share_capability['provisioned_capacity_gb'],
|
||||||
fake_pool.provisioned_capacity_gb)
|
fake_pool.provisioned_capacity_gb)
|
||||||
|
if 'ipv4_support' in share_capability:
|
||||||
|
self.assertEqual(share_capability['ipv4_support'],
|
||||||
|
fake_pool.ipv4_support)
|
||||||
|
if 'ipv6_support' in share_capability:
|
||||||
|
self.assertEqual(share_capability['ipv6_support'],
|
||||||
|
fake_pool.ipv6_support)
|
||||||
|
@ -336,6 +336,8 @@ class CephFSDriverTestCase(test.TestCase):
|
|||||||
self._driver._update_share_stats()
|
self._driver._update_share_stats()
|
||||||
result = self._driver._stats
|
result = self._driver._stats
|
||||||
|
|
||||||
|
self.assertTrue(result['ipv4_support'])
|
||||||
|
self.assertFalse(result['ipv6_support'])
|
||||||
self.assertEqual("CEPHFS", result['storage_protocol'])
|
self.assertEqual("CEPHFS", result['storage_protocol'])
|
||||||
|
|
||||||
def test_module_missing(self):
|
def test_module_missing(self):
|
||||||
|
@ -105,6 +105,8 @@ class ContainerShareDriverTestCase(test.TestCase):
|
|||||||
self.assertEqual('ContainerShareDriver',
|
self.assertEqual('ContainerShareDriver',
|
||||||
self._driver._stats['driver_name'])
|
self._driver._stats['driver_name'])
|
||||||
self.assertEqual('test-pool', self._driver._stats['pools'])
|
self.assertEqual('test-pool', self._driver._stats['pools'])
|
||||||
|
self.assertTrue(self._driver._stats['ipv4_support'])
|
||||||
|
self.assertFalse(self._driver._stats['ipv6_support'])
|
||||||
|
|
||||||
def test_create_share(self):
|
def test_create_share(self):
|
||||||
helper = mock.Mock()
|
helper = mock.Mock()
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
import mock
|
import mock
|
||||||
from stevedore import extension
|
from stevedore import extension
|
||||||
|
|
||||||
from manila import network
|
|
||||||
from manila.share import configuration as conf
|
from manila.share import configuration as conf
|
||||||
from manila.share.drivers.dell_emc import driver as emcdriver
|
from manila.share.drivers.dell_emc import driver as emcdriver
|
||||||
from manila.share.drivers.dell_emc.plugins import base
|
from manila.share.drivers.dell_emc.plugins import base
|
||||||
@ -98,7 +97,6 @@ class EMCShareFrameworkTestCase(test.TestCase):
|
|||||||
self.configuration.append_config_values = mock.Mock(return_value=0)
|
self.configuration.append_config_values = mock.Mock(return_value=0)
|
||||||
self.configuration.share_backend_name = FAKE_BACKEND
|
self.configuration.share_backend_name = FAKE_BACKEND
|
||||||
self.mock_object(self.configuration, 'safe_get', self._fake_safe_get)
|
self.mock_object(self.configuration, 'safe_get', self._fake_safe_get)
|
||||||
self.mock_object(network, 'API')
|
|
||||||
self.driver = emcdriver.EMCShareDriver(
|
self.driver = emcdriver.EMCShareDriver(
|
||||||
configuration=self.configuration)
|
configuration=self.configuration)
|
||||||
|
|
||||||
@ -133,6 +131,8 @@ class EMCShareFrameworkTestCase(test.TestCase):
|
|||||||
data['goodness_function'] = None
|
data['goodness_function'] = None
|
||||||
data['snapshot_support'] = True
|
data['snapshot_support'] = True
|
||||||
data['create_share_from_snapshot_support'] = True
|
data['create_share_from_snapshot_support'] = True
|
||||||
|
data['ipv4_support'] = True
|
||||||
|
data['ipv6_support'] = False
|
||||||
self.assertEqual(data, self.driver._stats)
|
self.assertEqual(data, self.driver._stats)
|
||||||
|
|
||||||
def _fake_safe_get(self, value):
|
def _fake_safe_get(self, value):
|
||||||
|
@ -265,6 +265,8 @@ class GlusterfsNativeShareDriverTestCase(test.TestCase):
|
|||||||
'replication_domain': None,
|
'replication_domain': None,
|
||||||
'filter_function': None,
|
'filter_function': None,
|
||||||
'goodness_function': None,
|
'goodness_function': None,
|
||||||
|
'ipv4_support': True,
|
||||||
|
'ipv6_support': False,
|
||||||
}
|
}
|
||||||
self.assertEqual(test_data, self._driver._stats)
|
self.assertEqual(test_data, self._driver._stats)
|
||||||
|
|
||||||
|
@ -409,9 +409,12 @@ class HDFSNativeShareDriverTestCase(test.TestCase):
|
|||||||
'free_capacity_gb', 'total_capacity_gb',
|
'free_capacity_gb', 'total_capacity_gb',
|
||||||
'driver_handles_share_servers',
|
'driver_handles_share_servers',
|
||||||
'reserved_percentage', 'vendor_name', 'storage_protocol',
|
'reserved_percentage', 'vendor_name', 'storage_protocol',
|
||||||
|
'ipv4_support', 'ipv6_support'
|
||||||
]
|
]
|
||||||
for key in expected_keys:
|
for key in expected_keys:
|
||||||
self.assertIn(key, result)
|
self.assertIn(key, result)
|
||||||
|
self.assertTrue(result['ipv4_support'])
|
||||||
|
self.assertFalse(False, result['ipv6_support'])
|
||||||
self.assertEqual('HDFS', result['storage_protocol'])
|
self.assertEqual('HDFS', result['storage_protocol'])
|
||||||
self._driver._get_available_capacity.assert_called_once_with()
|
self._driver._get_available_capacity.assert_called_once_with()
|
||||||
|
|
||||||
|
@ -747,6 +747,8 @@ class HPE3ParDriverTestCase(test.TestCase):
|
|||||||
'replication_domain': None,
|
'replication_domain': None,
|
||||||
'filter_function': None,
|
'filter_function': None,
|
||||||
'goodness_function': None,
|
'goodness_function': None,
|
||||||
|
'ipv4_support': True,
|
||||||
|
'ipv6_support': False,
|
||||||
}
|
}
|
||||||
|
|
||||||
result = self.driver.get_share_stats(refresh=True)
|
result = self.driver.get_share_stats(refresh=True)
|
||||||
@ -822,6 +824,8 @@ class HPE3ParDriverTestCase(test.TestCase):
|
|||||||
'replication_domain': None,
|
'replication_domain': None,
|
||||||
'filter_function': None,
|
'filter_function': None,
|
||||||
'goodness_function': None,
|
'goodness_function': None,
|
||||||
|
'ipv4_support': True,
|
||||||
|
'ipv6_support': False,
|
||||||
}
|
}
|
||||||
|
|
||||||
result = self.driver.get_share_stats(refresh=True)
|
result = self.driver.get_share_stats(refresh=True)
|
||||||
@ -864,6 +868,8 @@ class HPE3ParDriverTestCase(test.TestCase):
|
|||||||
'replication_domain': None,
|
'replication_domain': None,
|
||||||
'filter_function': None,
|
'filter_function': None,
|
||||||
'goodness_function': None,
|
'goodness_function': None,
|
||||||
|
'ipv4_support': True,
|
||||||
|
'ipv6_support': False,
|
||||||
}
|
}
|
||||||
|
|
||||||
result = self.driver.get_share_stats(refresh=True)
|
result = self.driver.get_share_stats(refresh=True)
|
||||||
|
@ -2431,6 +2431,8 @@ class HuaweiShareDriverTestCase(test.TestCase):
|
|||||||
"goodness_function": None,
|
"goodness_function": None,
|
||||||
"pools": [],
|
"pools": [],
|
||||||
"share_group_stats": {"consistent_snapshot_support": None},
|
"share_group_stats": {"consistent_snapshot_support": None},
|
||||||
|
"ipv4_support": True,
|
||||||
|
"ipv6_support": False,
|
||||||
}
|
}
|
||||||
|
|
||||||
if replication_support:
|
if replication_support:
|
||||||
|
@ -81,26 +81,55 @@ class NFSHelperTestCase(test.TestCase):
|
|||||||
self.server, ['sudo', 'exportfs'])
|
self.server, ['sudo', 'exportfs'])
|
||||||
|
|
||||||
@ddt.data(
|
@ddt.data(
|
||||||
{"public_address": "1.2.3.4"},
|
{"server": {"public_address": "1.2.3.4"}, "version": 4},
|
||||||
{"public_address": "1.2.3.4", "admin_ip": "5.6.7.8"},
|
{"server": {"public_address": "1001::1002"}, "version": 6},
|
||||||
{"public_address": "1.2.3.4", "ip": "9.10.11.12"},
|
{"server": {"public_address": "1.2.3.4", "admin_ip": "5.6.7.8"},
|
||||||
|
"version": 4},
|
||||||
|
{"server": {"public_address": "1.2.3.4", "ip": "9.10.11.12"},
|
||||||
|
"version": 4},
|
||||||
|
{"server": {"public_address": "1001::1001", "ip": "1001::1002"},
|
||||||
|
"version": 6},
|
||||||
|
{"server": {"public_address": "1001::1002", "admin_ip": "1001::1002"},
|
||||||
|
"version": 6},
|
||||||
|
{"server": {"public_addresses": ["1001::1002"]}, "version": 6},
|
||||||
|
{"server": {"public_addresses": ["1.2.3.4", "1001::1002"]},
|
||||||
|
"version": {"1.2.3.4": 4, "1001::1002": 6}},
|
||||||
)
|
)
|
||||||
def test_create_exports(self, server):
|
@ddt.unpack
|
||||||
|
def test_create_exports(self, server, version):
|
||||||
result = self._helper.create_exports(server, self.share_name)
|
result = self._helper.create_exports(server, self.share_name)
|
||||||
|
|
||||||
expected_export_locations = []
|
expected_export_locations = []
|
||||||
path = os.path.join(CONF.share_mount_path, self.share_name)
|
path = os.path.join(CONF.share_mount_path, self.share_name)
|
||||||
service_address = server.get("admin_ip", server.get("ip"))
|
service_address = server.get("admin_ip", server.get("ip"))
|
||||||
for ip, is_admin in ((server['public_address'], False),
|
version_copy = version
|
||||||
(service_address, True)):
|
|
||||||
if ip:
|
def convert_address(address, version):
|
||||||
expected_export_locations.append({
|
if version == 4:
|
||||||
"path": "%s:%s" % (ip, path),
|
return address
|
||||||
"is_admin_only": is_admin,
|
return "[%s]" % address
|
||||||
"metadata": {
|
|
||||||
"export_location_metadata_example": "example",
|
if 'public_addresses' in server:
|
||||||
},
|
pairs = list(map(lambda addr: (addr, False),
|
||||||
})
|
server['public_addresses']))
|
||||||
|
else:
|
||||||
|
pairs = [(server['public_address'], False)]
|
||||||
|
|
||||||
|
service_address = server.get("admin_ip", server.get("ip"))
|
||||||
|
if service_address:
|
||||||
|
pairs.append((service_address, True))
|
||||||
|
|
||||||
|
for ip, is_admin in pairs:
|
||||||
|
if isinstance(version_copy, dict):
|
||||||
|
version = version_copy.get(ip)
|
||||||
|
|
||||||
|
expected_export_locations.append({
|
||||||
|
"path": "%s:%s" % (convert_address(ip, version), path),
|
||||||
|
"is_admin_only": is_admin,
|
||||||
|
"metadata": {
|
||||||
|
"export_location_metadata_example": "example",
|
||||||
|
},
|
||||||
|
})
|
||||||
self.assertEqual(expected_export_locations, result)
|
self.assertEqual(expected_export_locations, result)
|
||||||
|
|
||||||
@ddt.data(const.ACCESS_LEVEL_RW, const.ACCESS_LEVEL_RO)
|
@ddt.data(const.ACCESS_LEVEL_RW, const.ACCESS_LEVEL_RO)
|
||||||
@ -135,19 +164,36 @@ class NFSHelperTestCase(test.TestCase):
|
|||||||
mock.call(self.server, ['sudo', 'exportfs', '-u',
|
mock.call(self.server, ['sudo', 'exportfs', '-u',
|
||||||
':'.join(['3.3.3.3', local_path])]),
|
':'.join(['3.3.3.3', local_path])]),
|
||||||
mock.call(self.server, ['sudo', 'exportfs', '-u',
|
mock.call(self.server, ['sudo', 'exportfs', '-u',
|
||||||
':'.join(['6.6.6.6/0.0.0.0',
|
':'.join(['6.6.6.6/0',
|
||||||
local_path])]),
|
local_path])]),
|
||||||
mock.call(self.server, ['sudo', 'exportfs', '-o',
|
mock.call(self.server, ['sudo', 'exportfs', '-o',
|
||||||
expected_mount_options % access_level,
|
expected_mount_options % access_level,
|
||||||
':'.join(['2.2.2.2', local_path])]),
|
':'.join(['2.2.2.2', local_path])]),
|
||||||
mock.call(self.server, ['sudo', 'exportfs', '-o',
|
mock.call(self.server, ['sudo', 'exportfs', '-o',
|
||||||
expected_mount_options % access_level,
|
expected_mount_options % access_level,
|
||||||
':'.join(['5.5.5.5/255.255.255.0',
|
':'.join(['5.5.5.5/24',
|
||||||
local_path])]),
|
local_path])]),
|
||||||
])
|
])
|
||||||
self._helper._sync_nfs_temp_and_perm_files.assert_has_calls([
|
self._helper._sync_nfs_temp_and_perm_files.assert_has_calls([
|
||||||
mock.call(self.server), mock.call(self.server)])
|
mock.call(self.server), mock.call(self.server)])
|
||||||
|
|
||||||
|
@ddt.data({'access': '10.0.0.1', 'result': '10.0.0.1'},
|
||||||
|
{'access': '10.0.0.1/32', 'result': '10.0.0.1'},
|
||||||
|
{'access': '10.0.0.0/24', 'result': '10.0.0.0/24'},
|
||||||
|
{'access': '1001::1001', 'result': '[1001::1001]'},
|
||||||
|
{'access': '1001::1000/128', 'result': '[1001::1000]'},
|
||||||
|
{'access': '1001::1000/124', 'result': '[1001::1000]/124'})
|
||||||
|
@ddt.unpack
|
||||||
|
def test__get_parsed_address_or_cidr(self, access, result):
|
||||||
|
self.assertEqual(result,
|
||||||
|
self._helper._get_parsed_address_or_cidr(access))
|
||||||
|
|
||||||
|
@ddt.data('10.0.0.265', '10.0.0.1/33', '1001::10069', '1001::1000/129')
|
||||||
|
def test__get_parsed_address_or_cidr_with_invalid_access(self, access):
|
||||||
|
self.assertRaises(exception.InvalidInput,
|
||||||
|
self._helper._get_parsed_address_or_cidr,
|
||||||
|
access)
|
||||||
|
|
||||||
def test_update_access_invalid_type(self):
|
def test_update_access_invalid_type(self):
|
||||||
access_rules = [test_generic.get_fake_access_rule(
|
access_rules = [test_generic.get_fake_access_rule(
|
||||||
'2.2.2.2', const.ACCESS_LEVEL_RW, access_type='fake'), ]
|
'2.2.2.2', const.ACCESS_LEVEL_RW, access_type='fake'), ]
|
||||||
@ -215,7 +261,8 @@ class NFSHelperTestCase(test.TestCase):
|
|||||||
self._helper._ssh_exec.assert_has_calls(
|
self._helper._ssh_exec.assert_has_calls(
|
||||||
[mock.call(self.server, mock.ANY) for i in range(1)])
|
[mock.call(self.server, mock.ANY) for i in range(1)])
|
||||||
|
|
||||||
@ddt.data('/foo/bar', '5.6.7.8:/bar/quuz', '5.6.7.9:/foo/quuz')
|
@ddt.data('/foo/bar', '5.6.7.8:/bar/quuz', '5.6.7.9:/foo/quuz',
|
||||||
|
'[1001::1001]:/foo/bar', '[1001::1000]/:124:/foo/bar')
|
||||||
def test_get_exports_for_share_single_ip(self, export_location):
|
def test_get_exports_for_share_single_ip(self, export_location):
|
||||||
server = dict(public_address='1.2.3.4')
|
server = dict(public_address='1.2.3.4')
|
||||||
|
|
||||||
@ -257,7 +304,8 @@ class NFSHelperTestCase(test.TestCase):
|
|||||||
exception.ManilaException,
|
exception.ManilaException,
|
||||||
self._helper.get_exports_for_share, server, export_location)
|
self._helper.get_exports_for_share, server, export_location)
|
||||||
|
|
||||||
@ddt.data('/foo/bar', '5.6.7.8:/foo/bar', '5.6.7.88:fake:/foo/bar')
|
@ddt.data('/foo/bar', '5.6.7.8:/foo/bar', '5.6.7.88:fake:/foo/bar',
|
||||||
|
'[1001::1002]:/foo/bar', '[1001::1000]/124:/foo/bar')
|
||||||
def test_get_share_path_by_export_location(self, export_location):
|
def test_get_share_path_by_export_location(self, export_location):
|
||||||
result = self._helper.get_share_path_by_export_location(
|
result = self._helper.get_share_path_by_export_location(
|
||||||
dict(), export_location)
|
dict(), export_location)
|
||||||
|
@ -416,6 +416,23 @@ class LVMShareDriverTestCase(test.TestCase):
|
|||||||
self.server, self.share['name'],
|
self.server, self.share['name'],
|
||||||
access_rules, add_rules=add_rules, delete_rules=delete_rules))
|
access_rules, add_rules=add_rules, delete_rules=delete_rules))
|
||||||
|
|
||||||
|
@ddt.data(('1001::1001/129', None, False), ('1.1.1.256', None, False),
|
||||||
|
('1001::1001', None, [6]), ('1.1.1.0', None, [4]),
|
||||||
|
(None, ['1001::1001', '1.1.1.0'], [6, 4]),
|
||||||
|
(None, ['1001::1001'], [6]), (None, ['1.1.1.0'], [4]),
|
||||||
|
(None, ['1001::1001/129', '1.1.1.0'], False))
|
||||||
|
@ddt.unpack
|
||||||
|
def test_get_configured_ip_version(
|
||||||
|
self, configured_ip, configured_ips, configured_ip_version):
|
||||||
|
CONF.set_default('lvm_share_export_ip', configured_ip)
|
||||||
|
CONF.set_default('lvm_share_export_ips', configured_ips)
|
||||||
|
if configured_ip_version:
|
||||||
|
self.assertEqual(configured_ip_version,
|
||||||
|
self._driver.get_configured_ip_version())
|
||||||
|
else:
|
||||||
|
self.assertRaises(exception.InvalidInput,
|
||||||
|
self._driver.get_configured_ip_version)
|
||||||
|
|
||||||
def test_mount_device(self):
|
def test_mount_device(self):
|
||||||
mount_path = self._get_mount_path(self.share)
|
mount_path = self._get_mount_path(self.share)
|
||||||
ret = self._driver._mount_device(self.share, 'fakedevice')
|
ret = self._driver._mount_device(self.share, 'fakedevice')
|
||||||
@ -541,7 +558,10 @@ class LVMShareDriverTestCase(test.TestCase):
|
|||||||
'count=1024', 'bs=1M',
|
'count=1024', 'bs=1M',
|
||||||
run_as_root=True)
|
run_as_root=True)
|
||||||
|
|
||||||
def test_update_share_stats(self):
|
@ddt.data(('1.1.1.1', 4), ('1001::1001', 6))
|
||||||
|
@ddt.unpack
|
||||||
|
def test_update_share_stats(self, configured_ip, version):
|
||||||
|
CONF.set_default('lvm_share_export_ip', configured_ip)
|
||||||
self.mock_object(self._driver, 'get_share_server_pools',
|
self.mock_object(self._driver, 'get_share_server_pools',
|
||||||
mock.Mock(return_value='test-pool'))
|
mock.Mock(return_value='test-pool'))
|
||||||
|
|
||||||
@ -552,6 +572,8 @@ class LVMShareDriverTestCase(test.TestCase):
|
|||||||
self.assertTrue(self._driver._stats['snapshot_support'])
|
self.assertTrue(self._driver._stats['snapshot_support'])
|
||||||
self.assertEqual('LVMShareDriver', self._driver._stats['driver_name'])
|
self.assertEqual('LVMShareDriver', self._driver._stats['driver_name'])
|
||||||
self.assertEqual('test-pool', self._driver._stats['pools'])
|
self.assertEqual('test-pool', self._driver._stats['pools'])
|
||||||
|
self.assertEqual(version == 4, self._driver._stats['ipv4_support'])
|
||||||
|
self.assertEqual(version == 6, self._driver._stats['ipv6_support'])
|
||||||
|
|
||||||
def test_revert_to_snapshot(self):
|
def test_revert_to_snapshot(self):
|
||||||
mock_update_access = self.mock_object(self._helper_nfs,
|
mock_update_access = self.mock_object(self._helper_nfs,
|
||||||
|
@ -779,8 +779,10 @@ class ServiceInstanceManagerTestCase(test.TestCase):
|
|||||||
fake_server_details)
|
fake_server_details)
|
||||||
|
|
||||||
@ddt.data(
|
@ddt.data(
|
||||||
*[{'s': s, 't': t, 'server': server}
|
*[{'service_config': service_config,
|
||||||
for s, t in (
|
'tenant_config': tenant_config,
|
||||||
|
'server': server}
|
||||||
|
for service_config, tenant_config in (
|
||||||
('fake_net_s', 'fake_net_t'),
|
('fake_net_s', 'fake_net_t'),
|
||||||
('fake_net_s', '12.34.56.78'),
|
('fake_net_s', '12.34.56.78'),
|
||||||
('98.76.54.123', 'fake_net_t'),
|
('98.76.54.123', 'fake_net_t'),
|
||||||
@ -800,18 +802,25 @@ class ServiceInstanceManagerTestCase(test.TestCase):
|
|||||||
{'addr': 'fake4'}],
|
{'addr': 'fake4'}],
|
||||||
}})])
|
}})])
|
||||||
@ddt.unpack
|
@ddt.unpack
|
||||||
def test_get_common_server_valid_cases(self, s, t, server):
|
def test_get_common_server_valid_cases(self, service_config,
|
||||||
self._get_common_server(s, t, server, True)
|
tenant_config, server):
|
||||||
|
self._get_common_server(service_config, tenant_config, server,
|
||||||
|
'98.76.54.123', '12.34.56.78', True)
|
||||||
|
|
||||||
@ddt.data(
|
@ddt.data(
|
||||||
*[{'s': s, 't': t, 'server': server}
|
*[{'service_config': service_config,
|
||||||
for s, t in (
|
'tenant_config': tenant_config,
|
||||||
|
'server': server}
|
||||||
|
for service_config, tenant_config in (
|
||||||
('fake_net_s', 'fake'),
|
('fake_net_s', 'fake'),
|
||||||
('fake', 'fake_net_t'),
|
('fake', 'fake_net_t'),
|
||||||
('fake', 'fake'),
|
('fake', 'fake'),
|
||||||
('98.76.54.123', '12.12.12.1212'),
|
('98.76.54.123', '12.12.12.1212'),
|
||||||
('12.12.12.1212', '12.34.56.78'),
|
('12.12.12.1212', '12.34.56.78'),
|
||||||
('12.12.12.1212', '12.12.12.1212'))
|
('12.12.12.1212', '12.12.12.1212'),
|
||||||
|
('1001::1001', '1001::100G'),
|
||||||
|
('1001::10G1', '1001::1001'),
|
||||||
|
)
|
||||||
for server in (
|
for server in (
|
||||||
{'networks': {
|
{'networks': {
|
||||||
'fake_net_s': ['foo', '98.76.54.123', 'bar'],
|
'fake_net_s': ['foo', '98.76.54.123', 'bar'],
|
||||||
@ -827,15 +836,38 @@ class ServiceInstanceManagerTestCase(test.TestCase):
|
|||||||
{'addr': 'fake4'}],
|
{'addr': 'fake4'}],
|
||||||
}})])
|
}})])
|
||||||
@ddt.unpack
|
@ddt.unpack
|
||||||
def test_get_common_server_invalid_cases(self, s, t, server):
|
def test_get_common_server_invalid_cases(self, service_config,
|
||||||
self._get_common_server(s, t, server, False)
|
tenant_config, server):
|
||||||
|
self._get_common_server(service_config, tenant_config, server,
|
||||||
|
'98.76.54.123', '12.34.56.78', False)
|
||||||
|
|
||||||
def _get_common_server(self, s, t, server, is_valid=True):
|
@ddt.data(
|
||||||
|
*[{'service_config': service_config,
|
||||||
|
'tenant_config': tenant_config,
|
||||||
|
'server': server}
|
||||||
|
for service_config, tenant_config in (
|
||||||
|
('fake_net_s', '1001::1002'),
|
||||||
|
('1001::1001', 'fake_net_t'),
|
||||||
|
('1001::1001', '1001::1002'))
|
||||||
|
for server in (
|
||||||
|
{'networks': {
|
||||||
|
'fake_net_s': ['foo', '1001::1001'],
|
||||||
|
'fake_net_t': ['bar', '1001::1002']}},
|
||||||
|
{'addresses': {
|
||||||
|
'fake_net_s': [{'addr': 'foo'}, {'addr': '1001::1001'}],
|
||||||
|
'fake_net_t': [{'addr': 'bar'}, {'addr': '1001::1002'}]}})])
|
||||||
|
@ddt.unpack
|
||||||
|
def test_get_common_server_valid_ipv6_address(self, service_config,
|
||||||
|
tenant_config, server):
|
||||||
|
self._get_common_server(service_config, tenant_config, server,
|
||||||
|
'1001::1001', '1001::1002', True)
|
||||||
|
|
||||||
|
def _get_common_server(self, service_config, tenant_config,
|
||||||
|
server, service_address, network_address,
|
||||||
|
is_valid=True):
|
||||||
fake_instance_id = 'fake_instance_id'
|
fake_instance_id = 'fake_instance_id'
|
||||||
fake_user = 'fake_user'
|
fake_user = 'fake_user'
|
||||||
fake_pass = 'fake_pass'
|
fake_pass = 'fake_pass'
|
||||||
fake_addr_s = '98.76.54.123'
|
|
||||||
fake_addr_t = '12.34.56.78'
|
|
||||||
fake_server = {'id': fake_instance_id}
|
fake_server = {'id': fake_instance_id}
|
||||||
fake_server.update(server)
|
fake_server.update(server)
|
||||||
expected = {
|
expected = {
|
||||||
@ -843,17 +875,17 @@ class ServiceInstanceManagerTestCase(test.TestCase):
|
|||||||
'username': fake_user,
|
'username': fake_user,
|
||||||
'password': fake_pass,
|
'password': fake_pass,
|
||||||
'pk_path': self._manager.path_to_private_key,
|
'pk_path': self._manager.path_to_private_key,
|
||||||
'ip': fake_addr_s,
|
'ip': service_address,
|
||||||
'public_address': fake_addr_t,
|
'public_address': network_address,
|
||||||
'instance_id': fake_instance_id,
|
'instance_id': fake_instance_id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def fake_get_config_option(attr):
|
def fake_get_config_option(attr):
|
||||||
if attr == 'service_net_name_or_ip':
|
if attr == 'service_net_name_or_ip':
|
||||||
return s
|
return service_config
|
||||||
elif attr == 'tenant_net_name_or_ip':
|
elif attr == 'tenant_net_name_or_ip':
|
||||||
return t
|
return tenant_config
|
||||||
elif attr == 'service_instance_name_or_id':
|
elif attr == 'service_instance_name_or_id':
|
||||||
return fake_instance_id
|
return fake_instance_id
|
||||||
elif attr == 'service_instance_user':
|
elif attr == 'service_instance_user':
|
||||||
|
@ -362,6 +362,8 @@ class ZFSonLinuxShareDriverTestCase(test.TestCase):
|
|||||||
'vendor_name': 'Open Source',
|
'vendor_name': 'Open Source',
|
||||||
'filter_function': None,
|
'filter_function': None,
|
||||||
'goodness_function': None,
|
'goodness_function': None,
|
||||||
|
'ipv4_support': True,
|
||||||
|
'ipv6_support': False,
|
||||||
}
|
}
|
||||||
if replication_domain:
|
if replication_domain:
|
||||||
expected['replication_type'] = 'readable'
|
expected['replication_type'] = 'readable'
|
||||||
|
@ -720,3 +720,56 @@ class ShareInstanceAccessTestCase(test.TestCase):
|
|||||||
else:
|
else:
|
||||||
self.assertEqual(states[0], rule_1['state'])
|
self.assertEqual(states[0], rule_1['state'])
|
||||||
self.assertEqual(states[-1], rule_4['state'])
|
self.assertEqual(states[-1], rule_4['state'])
|
||||||
|
|
||||||
|
@ddt.data(('nfs', True), ('cifs', False), ('ceph', False))
|
||||||
|
@ddt.unpack
|
||||||
|
def test__filter_ipv6_rules(self, proto, filtered):
|
||||||
|
pass_rules = [
|
||||||
|
{
|
||||||
|
'access_type': 'ip',
|
||||||
|
'access_to': '1.1.1.1'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'access_type': 'ip',
|
||||||
|
'access_to': '1.1.1.0/24'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'access_type': 'user',
|
||||||
|
'access_to': 'fake_user'
|
||||||
|
},
|
||||||
|
]
|
||||||
|
fail_rules = [
|
||||||
|
{
|
||||||
|
'access_type': 'ip',
|
||||||
|
'access_to': '1001::1001'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'access_type': 'ip',
|
||||||
|
'access_to': '1001::/64'
|
||||||
|
},
|
||||||
|
]
|
||||||
|
test_rules = pass_rules + fail_rules
|
||||||
|
filtered_rules = self.access_helper._filter_ipv6_rules(
|
||||||
|
test_rules, proto)
|
||||||
|
if filtered:
|
||||||
|
self.assertEqual(pass_rules, filtered_rules)
|
||||||
|
else:
|
||||||
|
self.assertEqual(test_rules, filtered_rules)
|
||||||
|
|
||||||
|
def test__get_rules_to_send_to_driver(self):
|
||||||
|
self.driver.ipv6_implemented = False
|
||||||
|
|
||||||
|
share = db_utils.create_share(status=constants.STATUS_AVAILABLE)
|
||||||
|
share_instance = share['instance']
|
||||||
|
db_utils.create_access(share_id=share['id'], access_to='1001::/64',
|
||||||
|
state=constants.ACCESS_STATE_ACTIVE)
|
||||||
|
self.mock_object(
|
||||||
|
self.access_helper, 'get_and_update_share_instance_access_rules',
|
||||||
|
mock.Mock(side_effect=self.access_helper.
|
||||||
|
get_and_update_share_instance_access_rules))
|
||||||
|
|
||||||
|
access_rules_to_be_on_share, add_rules, delete_rules = (
|
||||||
|
self.access_helper._get_rules_to_send_to_driver(
|
||||||
|
self.context, share_instance))
|
||||||
|
self.assertEqual([], add_rules)
|
||||||
|
self.assertEqual([], delete_rules)
|
||||||
|
@ -19,6 +19,7 @@ import time
|
|||||||
|
|
||||||
import ddt
|
import ddt
|
||||||
import mock
|
import mock
|
||||||
|
from mock import PropertyMock
|
||||||
|
|
||||||
from manila import exception
|
from manila import exception
|
||||||
from manila import network
|
from manila import network
|
||||||
@ -1083,3 +1084,71 @@ class ShareDriverTestCase(test.TestCase):
|
|||||||
share_driver.snapshot_update_access,
|
share_driver.snapshot_update_access,
|
||||||
'fake_context', 'fake_snapshot', ['r1', 'r2'],
|
'fake_context', 'fake_snapshot', ['r1', 'r2'],
|
||||||
[], [])
|
[], [])
|
||||||
|
|
||||||
|
@ddt.data({'capability': (True, True),
|
||||||
|
'user_admin_networks': [[4], [4]],
|
||||||
|
'expected': {'ipv4': True, 'ipv6': False}},
|
||||||
|
{'capability': (True, True),
|
||||||
|
'user_admin_networks': [[6], [6]],
|
||||||
|
'expected': {'ipv4': False, 'ipv6': True}},
|
||||||
|
{'capability': (False, False),
|
||||||
|
'user_admin_networks': [[4], [4]],
|
||||||
|
'expected': {'ipv4': False, 'ipv6': False}},
|
||||||
|
{'capability': (True, True),
|
||||||
|
'user_admin_networks': [[4], [6]],
|
||||||
|
'expected': {'ipv4': False, 'ipv6': False}},
|
||||||
|
{'capability': (False, False),
|
||||||
|
'user_admin_networks': [[6], [4]],
|
||||||
|
'expected': {'ipv4': False, 'ipv6': False}},)
|
||||||
|
@ddt.unpack
|
||||||
|
def test_add_ip_version_capability_if_dhss_true(self, capability,
|
||||||
|
user_admin_networks,
|
||||||
|
expected):
|
||||||
|
share_driver = self._instantiate_share_driver(None, True)
|
||||||
|
version = PropertyMock(side_effect=user_admin_networks)
|
||||||
|
type(share_driver.network_api).enabled_ip_version = version
|
||||||
|
data = {'share_backend_name': 'fake_backend',
|
||||||
|
'ipv4_support': capability[0],
|
||||||
|
'ipv6_support': capability[1]}
|
||||||
|
|
||||||
|
result = share_driver.add_ip_version_capability(data)
|
||||||
|
|
||||||
|
self.assertIsNotNone(result['ipv4_support'])
|
||||||
|
self.assertEqual(expected['ipv4'], result['ipv4_support'])
|
||||||
|
self.assertIsNotNone(result['ipv6_support'])
|
||||||
|
self.assertEqual(expected['ipv6'], result['ipv6_support'])
|
||||||
|
|
||||||
|
@ddt.data({'capability': (True, False),
|
||||||
|
'conf': [4],
|
||||||
|
'expected': {'ipv4': True, 'ipv6': False}},
|
||||||
|
{'capability': (True, True),
|
||||||
|
'conf': [6],
|
||||||
|
'expected': {'ipv4': False, 'ipv6': True}},
|
||||||
|
{'capability': (False, False),
|
||||||
|
'conf': [4],
|
||||||
|
'expected': {'ipv4': False, 'ipv6': False}},
|
||||||
|
{'capability': (False, True),
|
||||||
|
'conf': [4],
|
||||||
|
'expected': {'ipv4': False, 'ipv6': False}},
|
||||||
|
{'capability': (False, True),
|
||||||
|
'conf': [6],
|
||||||
|
'expected': {'ipv4': False, 'ipv6': True}},
|
||||||
|
{'capability': (True, True),
|
||||||
|
'conf': [4, 6],
|
||||||
|
'expected': {'ipv4': True, 'ipv6': True}},
|
||||||
|
)
|
||||||
|
@ddt.unpack
|
||||||
|
def test_add_ip_version_capability_if_dhss_false(self, capability,
|
||||||
|
conf, expected):
|
||||||
|
share_driver = self._instantiate_share_driver(None, False)
|
||||||
|
self.mock_object(share_driver, 'get_configured_ip_version',
|
||||||
|
mock.Mock(return_value=conf))
|
||||||
|
data = {'share_backend_name': 'fake_backend',
|
||||||
|
'ipv4_support': capability[0],
|
||||||
|
'ipv6_support': capability[1]}
|
||||||
|
result = share_driver.add_ip_version_capability(data)
|
||||||
|
|
||||||
|
self.assertIsNotNone(result['ipv4_support'])
|
||||||
|
self.assertEqual(expected['ipv4'], result['ipv4_support'])
|
||||||
|
self.assertIsNotNone(result['ipv6_support'])
|
||||||
|
self.assertEqual(expected['ipv6'], result['ipv6_support'])
|
||||||
|
@ -128,3 +128,30 @@ class NetworkBaseAPITestCase(test.TestCase):
|
|||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
exception.NetworkBadConfigurationException,
|
exception.NetworkBadConfigurationException,
|
||||||
result._verify_share_network, 'foo_id', None)
|
result._verify_share_network, 'foo_id', None)
|
||||||
|
|
||||||
|
@ddt.data((True, False, 6), (False, True, 4),
|
||||||
|
(True, True, 6), (None, None, False))
|
||||||
|
@ddt.unpack
|
||||||
|
def test_enabled_ip_version(self, network_plugin_ipv6_enabled,
|
||||||
|
network_plugin_ipv4_enabled,
|
||||||
|
enable_ip_version):
|
||||||
|
class FakeNetworkAPI(network.NetworkBaseAPI):
|
||||||
|
def allocate_network(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def deallocate_network(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
network.CONF.set_default('network_plugin_ipv6_enabled',
|
||||||
|
network_plugin_ipv6_enabled)
|
||||||
|
network.CONF.set_default('network_plugin_ipv4_enabled',
|
||||||
|
network_plugin_ipv4_enabled)
|
||||||
|
|
||||||
|
result = FakeNetworkAPI()
|
||||||
|
|
||||||
|
if enable_ip_version:
|
||||||
|
self.assertTrue(hasattr(result, 'enabled_ip_version'))
|
||||||
|
self.assertEqual(enable_ip_version, result.enabled_ip_version)
|
||||||
|
else:
|
||||||
|
self.assertRaises(exception.NetworkBadConfigurationException,
|
||||||
|
getattr, result, 'enabled_ip_version')
|
||||||
|
@ -386,14 +386,22 @@ def cidr_to_netmask(cidr):
|
|||||||
|
|
||||||
|
|
||||||
def is_valid_ip_address(ip_address, ip_version):
|
def is_valid_ip_address(ip_address, ip_version):
|
||||||
if int(ip_version) == 4:
|
ip_version = ([int(ip_version)] if not isinstance(ip_version, list)
|
||||||
return netutils.is_valid_ipv4(ip_address)
|
else ip_version)
|
||||||
elif int(ip_version) == 6:
|
|
||||||
return netutils.is_valid_ipv6(ip_address)
|
if not set(ip_version).issubset(set([4, 6])):
|
||||||
else:
|
|
||||||
raise exception.ManilaException(
|
raise exception.ManilaException(
|
||||||
_("Provided improper IP version '%s'.") % ip_version)
|
_("Provided improper IP version '%s'.") % ip_version)
|
||||||
|
|
||||||
|
if 4 in ip_version:
|
||||||
|
if netutils.is_valid_ipv4(ip_address):
|
||||||
|
return True
|
||||||
|
if 6 in ip_version:
|
||||||
|
if netutils.is_valid_ipv6(ip_address):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
class IsAMatcher(object):
|
class IsAMatcher(object):
|
||||||
def __init__(self, expected_value=None):
|
def __init__(self, expected_value=None):
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- Added optional extra spec 'ipv4_support' and 'ipv6_support' for share
|
||||||
|
type.
|
||||||
|
- Added new capabilities 'ipv4_support' and 'ipv6_support' for IP based
|
||||||
|
drivers.
|
||||||
|
- Added IPv6 support in network plugins. (support either IPv6 or IPv4)
|
||||||
|
- Added IPv6 support in the lvm driver. (support both IPv6 and IPv4)
|
Loading…
Reference in New Issue
Block a user