Fix NFSHelper 0-length netmask bug
Add special case handling of 0-length netmasks in NFS helper. This is required because nfs-utils on Linux support CIDR style network access unless the netmask is length 0. Also split out utility function for wrapping IPv6 addresses with square brackets. Closes-bug: #1707946 Change-Id: Id907478837099b250a6c88b6b5ff50c214bcdbb2
This commit is contained in:
parent
5084efe621
commit
e3b6f4a40e
manila
releasenotes/notes
@ -14,9 +14,10 @@
|
||||
# under the License.
|
||||
|
||||
import copy
|
||||
import netaddr
|
||||
import ipaddress
|
||||
import os
|
||||
import re
|
||||
import six
|
||||
|
||||
from oslo_log import log
|
||||
|
||||
@ -190,16 +191,24 @@ class NFSHelper(NASHelperBase):
|
||||
if 'public_addresses' in server_copy:
|
||||
for address in server_copy['public_addresses']:
|
||||
public_addresses.append(
|
||||
self._get_parsed_address_or_cidr(address))
|
||||
self._escaped_address(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)
|
||||
server_copy[t] = self._escaped_address(address)
|
||||
|
||||
return self.get_exports_for_share(server_copy, path)
|
||||
|
||||
@staticmethod
|
||||
def _escaped_address(address):
|
||||
addr = ipaddress.ip_address(six.text_type(address))
|
||||
if addr.version == 4:
|
||||
return six.text_type(addr)
|
||||
else:
|
||||
return '[%s]' % six.text_type(addr)
|
||||
|
||||
def init_helper(self, server):
|
||||
try:
|
||||
self._ssh_exec(server, ['sudo', 'exportfs'])
|
||||
@ -239,13 +248,13 @@ class NFSHelper(NASHelperBase):
|
||||
rules_options = '%s,no_subtree_check'
|
||||
if access['access_level'] == const.ACCESS_LEVEL_RW:
|
||||
rules_options = ','.join((rules_options, 'no_root_squash'))
|
||||
access_to = self._get_parsed_address_or_cidr(
|
||||
access['access_to'])
|
||||
self._ssh_exec(
|
||||
server,
|
||||
['sudo', 'exportfs', '-o',
|
||||
rules_options % access['access_level'],
|
||||
':'.join((
|
||||
self._get_parsed_address_or_cidr(access['access_to']),
|
||||
local_path))])
|
||||
':'.join((access_to, local_path))])
|
||||
self._sync_nfs_temp_and_perm_files(server)
|
||||
# Adding/Deleting specific rules
|
||||
else:
|
||||
@ -255,7 +264,7 @@ class NFSHelper(NASHelperBase):
|
||||
(const.ACCESS_LEVEL_RO, const.ACCESS_LEVEL_RW))
|
||||
|
||||
for access in delete_rules:
|
||||
access['access_to'] = self._get_parsed_address_or_cidr(
|
||||
access_to = self._get_parsed_address_or_cidr(
|
||||
access['access_to'])
|
||||
try:
|
||||
self.validate_access_rules(
|
||||
@ -271,15 +280,15 @@ class NFSHelper(NASHelperBase):
|
||||
'to': access['access_to']})
|
||||
continue
|
||||
self._ssh_exec(server, ['sudo', 'exportfs', '-u',
|
||||
':'.join((access['access_to'], local_path))])
|
||||
':'.join((access_to, local_path))])
|
||||
if delete_rules:
|
||||
self._sync_nfs_temp_and_perm_files(server)
|
||||
for access in add_rules:
|
||||
access['access_to'] = self._get_parsed_address_or_cidr(
|
||||
access_to = self._get_parsed_address_or_cidr(
|
||||
access['access_to'])
|
||||
found_item = re.search(
|
||||
re.escape(local_path) + '[\s\n]*' + re.escape(
|
||||
access['access_to']), out)
|
||||
re.escape(local_path) + '[\s\n]*' + re.escape(access_to),
|
||||
out)
|
||||
if found_item is not None:
|
||||
LOG.warning("Access rule %(type)s:%(to)s already "
|
||||
"exists for share %(name)s" % {
|
||||
@ -296,18 +305,18 @@ class NFSHelper(NASHelperBase):
|
||||
server,
|
||||
['sudo', 'exportfs', '-o',
|
||||
rules_options % access['access_level'],
|
||||
':'.join((access['access_to'], local_path))])
|
||||
':'.join((access_to, local_path))])
|
||||
if add_rules:
|
||||
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]
|
||||
@staticmethod
|
||||
def _get_parsed_address_or_cidr(access_to):
|
||||
network = ipaddress.ip_network(six.text_type(access_to))
|
||||
mask_length = network.prefixlen
|
||||
address = six.text_type(network.network_address)
|
||||
if mask_length == 0:
|
||||
# Special case because Linux exports don't support /0 netmasks
|
||||
return '*'
|
||||
if network.version == 4:
|
||||
if mask_length == 32:
|
||||
return address
|
||||
|
@ -150,11 +150,11 @@ class NFSHelperTestCase(test.TestCase):
|
||||
add_rules = [
|
||||
test_generic.get_fake_access_rule('2.2.2.2', access_level),
|
||||
test_generic.get_fake_access_rule('2.2.2.3', access_level),
|
||||
test_generic.get_fake_access_rule('5.5.5.5/24', access_level)]
|
||||
test_generic.get_fake_access_rule('5.5.5.0/24', access_level)]
|
||||
delete_rules = [
|
||||
test_generic.get_fake_access_rule('3.3.3.3', access_level),
|
||||
test_generic.get_fake_access_rule('4.4.4.4', access_level, 'user'),
|
||||
test_generic.get_fake_access_rule('6.6.6.6/0', access_level)]
|
||||
test_generic.get_fake_access_rule('0.0.0.0/0', access_level)]
|
||||
self._helper.update_access(self.server, self.share_name, access_rules,
|
||||
add_rules=add_rules,
|
||||
delete_rules=delete_rules)
|
||||
@ -164,14 +164,14 @@ class NFSHelperTestCase(test.TestCase):
|
||||
mock.call(self.server, ['sudo', 'exportfs', '-u',
|
||||
':'.join(['3.3.3.3', local_path])]),
|
||||
mock.call(self.server, ['sudo', 'exportfs', '-u',
|
||||
':'.join(['6.6.6.6/0',
|
||||
':'.join(['*',
|
||||
local_path])]),
|
||||
mock.call(self.server, ['sudo', 'exportfs', '-o',
|
||||
expected_mount_options % access_level,
|
||||
':'.join(['2.2.2.2', local_path])]),
|
||||
mock.call(self.server, ['sudo', 'exportfs', '-o',
|
||||
expected_mount_options % access_level,
|
||||
':'.join(['5.5.5.5/24',
|
||||
':'.join(['5.5.5.0/24',
|
||||
local_path])]),
|
||||
])
|
||||
self._helper._sync_nfs_temp_and_perm_files.assert_has_calls([
|
||||
@ -190,7 +190,7 @@ class NFSHelperTestCase(test.TestCase):
|
||||
|
||||
@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.assertRaises(ValueError,
|
||||
self._helper._get_parsed_address_or_cidr,
|
||||
access)
|
||||
|
||||
|
@ -0,0 +1,5 @@
|
||||
---
|
||||
fixes:
|
||||
- Fixed application of access rules with type ``ip`` and netmask length 0 in
|
||||
the ``NFSHelper`` plugin, affecting LVM and Generic drivers. Previously
|
||||
these rules silently failed to apply.
|
Loading…
Reference in New Issue
Block a user