Merge "Enable IPv6 in manila(allow access)"

This commit is contained in:
Jenkins 2017-07-18 14:50:45 +00:00 committed by Gerrit Code Review
commit 6a9b665b5c
15 changed files with 188 additions and 140 deletions

View File

@ -13,12 +13,15 @@
# 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 os import os
import re import re
import six
import string import string
from oslo_config import cfg from oslo_config import cfg
from oslo_log import log from oslo_log import log
from oslo_utils import encodeutils
from six.moves.urllib import parse from six.moves.urllib import parse
import webob import webob
@ -343,32 +346,6 @@ def validate_username(access):
raise webob.exc.HTTPBadRequest(explanation=exc_str) raise webob.exc.HTTPBadRequest(explanation=exc_str)
def validate_ip_range(ip_range):
ip_range = ip_range.split('/')
exc_str = ('Supported ip format examples:\n'
'\t10.0.0.2, 10.0.0.0/24')
if len(ip_range) > 2:
raise webob.exc.HTTPBadRequest(explanation=exc_str)
if len(ip_range) == 2:
try:
prefix = int(ip_range[1])
if prefix < 0 or prefix > 32:
raise ValueError()
except ValueError:
msg = 'IP prefix should be in range from 0 to 32.'
raise webob.exc.HTTPBadRequest(explanation=msg)
ip_range = ip_range[0].split('.')
if len(ip_range) != 4:
raise webob.exc.HTTPBadRequest(explanation=exc_str)
for item in ip_range:
try:
if 0 <= int(item) <= 255:
continue
raise ValueError()
except ValueError:
raise webob.exc.HTTPBadRequest(explanation=exc_str)
def validate_cephx_id(cephx_id): def validate_cephx_id(cephx_id):
if not cephx_id: if not cephx_id:
raise webob.exc.HTTPBadRequest(explanation=_( raise webob.exc.HTTPBadRequest(explanation=_(
@ -389,14 +366,27 @@ def validate_cephx_id(cephx_id):
'Ceph IDs may not contain periods.')) 'Ceph IDs may not contain periods.'))
def validate_ip(access_to, enable_ipv6):
try:
if enable_ipv6:
validator = ipaddress.ip_network
else:
validator = ipaddress.IPv4Network
validator(six.text_type(access_to))
except ValueError as error:
err_msg = encodeutils.exception_to_unicode(error)
raise webob.exc.HTTPBadRequest(explanation=err_msg)
def validate_access(*args, **kwargs): def validate_access(*args, **kwargs):
access_type = kwargs.get('access_type') access_type = kwargs.get('access_type')
access_to = kwargs.get('access_to') access_to = kwargs.get('access_to')
enable_ceph = kwargs.get('enable_ceph') enable_ceph = kwargs.get('enable_ceph')
enable_ipv6 = kwargs.get('enable_ipv6')
if access_type == 'ip': if access_type == 'ip':
validate_ip_range(access_to) validate_ip(access_to, enable_ipv6)
elif access_type == 'user': elif access_type == 'user':
validate_username(access_to) validate_username(access_to)
elif access_type == 'cert': elif access_type == 'cert':

View File

@ -107,6 +107,8 @@ REST_API_VERSION_HISTORY = """
* 2.36 - Added like filter support in ``shares``, ``snapshots``, * 2.36 - Added like filter support in ``shares``, ``snapshots``,
``share-networks``, ``share-groups`` list APIs. ``share-networks``, ``share-groups`` list APIs.
* 2.37 - Added /messages APIs. * 2.37 - Added /messages APIs.
* 2.38 - Support IPv6 validation in allow_access API to enable IPv6 in
manila.
""" """
@ -114,7 +116,7 @@ REST_API_VERSION_HISTORY = """
# The default api version request is defined to be the # The default api version request is defined to be the
# minimum version of the API supported. # minimum version of the API supported.
_MIN_API_VERSION = "2.0" _MIN_API_VERSION = "2.0"
_MAX_API_VERSION = "2.37" _MAX_API_VERSION = "2.38"
DEFAULT_API_VERSION = _MIN_API_VERSION DEFAULT_API_VERSION = _MIN_API_VERSION

View File

@ -214,3 +214,7 @@ user documentation.
2.37 2.37
---- ----
Added /messages APIs. Added /messages APIs.
2.38
----
Support IPv6 format validation in allow_access API to enable IPv6.

View File

@ -372,7 +372,7 @@ class ShareMixin(object):
@wsgi.Controller.authorize('allow_access') @wsgi.Controller.authorize('allow_access')
def _allow_access(self, req, id, body, enable_ceph=False, def _allow_access(self, req, id, body, enable_ceph=False,
allow_on_error_status=False): allow_on_error_status=False, enable_ipv6=False):
"""Add share access rule.""" """Add share access rule."""
context = req.environ['manila.context'] context = req.environ['manila.context']
access_data = body.get('allow_access', body.get('os-allow_access')) access_data = body.get('allow_access', body.get('os-allow_access'))
@ -394,7 +394,8 @@ class ShareMixin(object):
access_to = access_data['access_to'] access_to = access_data['access_to']
common.validate_access(access_type=access_type, common.validate_access(access_type=access_type,
access_to=access_to, access_to=access_to,
enable_ceph=enable_ceph) enable_ceph=enable_ceph,
enable_ipv6=enable_ipv6)
try: try:
access = self.share_api.allow_access( access = self.share_api.allow_access(
context, share, access_type, access_to, context, share, access_type, access_to,

View File

@ -159,7 +159,7 @@ class ShareSnapshotsController(share_snapshots.ShareSnapshotMixin,
msg = _("Required parameter %s is empty.") % parameter msg = _("Required parameter %s is empty.") % parameter
raise exc_response(explanation=msg) raise exc_response(explanation=msg)
def _allow(self, req, id, body): def _allow(self, req, id, body, enable_ipv6=False):
context = req.environ['manila.context'] context = req.environ['manila.context']
if not (body and self.is_valid_body(body, 'allow_access')): if not (body and self.is_valid_body(body, 'allow_access')):
@ -175,7 +175,9 @@ class ShareSnapshotsController(share_snapshots.ShareSnapshotMixin,
access_type = access_data['access_type'] access_type = access_data['access_type']
access_to = access_data['access_to'] access_to = access_data['access_to']
common.validate_access(access_type=access_type, access_to=access_to) common.validate_access(access_type=access_type,
access_to=access_to,
enable_ipv6=enable_ipv6)
snapshot = self.share_api.get_snapshot(context, id) snapshot = self.share_api.get_snapshot(context, id)
@ -270,7 +272,10 @@ class ShareSnapshotsController(share_snapshots.ShareSnapshotMixin,
@wsgi.response(202) @wsgi.response(202)
@wsgi.Controller.authorize @wsgi.Controller.authorize
def allow_access(self, req, id, body=None): def allow_access(self, req, id, body=None):
return self._allow(req, id, body) enable_ipv6 = False
if req.api_version_request >= api_version.APIVersionRequest("2.38"):
enable_ipv6 = True
return self._allow(req, id, body, enable_ipv6)
@wsgi.Controller.api_version('2.32') @wsgi.Controller.api_version('2.32')
@wsgi.action('deny_access') @wsgi.action('deny_access')

View File

@ -343,6 +343,8 @@ class ShareController(shares.ShareMixin,
kwargs['enable_ceph'] = True kwargs['enable_ceph'] = True
if req.api_version_request >= api_version.APIVersionRequest("2.28"): if req.api_version_request >= api_version.APIVersionRequest("2.28"):
kwargs['allow_on_error_status'] = True kwargs['allow_on_error_status'] = True
if req.api_version_request >= api_version.APIVersionRequest("2.38"):
kwargs['enable_ipv6'] = True
return self._allow_access(*args, **kwargs) return self._allow_access(*args, **kwargs)

View File

@ -259,19 +259,23 @@ class MiscFunctionsTest(test.TestCase):
def test_validate_cephx_id_valid(self, test_id): def test_validate_cephx_id_valid(self, test_id):
common.validate_cephx_id(test_id) common.validate_cephx_id(test_id)
@ddt.data(['ip', '1.1.1.1', False], ['user', 'alice', False], @ddt.data(['ip', '1.1.1.1', False, False], ['user', 'alice', False, False],
['cert', 'alice', False], ['cephx', 'alice', True], ['cert', 'alice', False, False], ['cephx', 'alice', True, False],
['ip', '172.24.41.0/24', False],) ['ip', '172.24.41.0/24', False, False],
['ip', '1001::1001', False, True],
['ip', '1001::1000/120', False, True])
@ddt.unpack @ddt.unpack
def test_validate_access(self, access_type, access_to, ceph): def test_validate_access(self, access_type, access_to, ceph, enable_ipv6):
common.validate_access(access_type=access_type, access_to=access_to, common.validate_access(access_type=access_type, access_to=access_to,
enable_ceph=ceph) enable_ceph=ceph, enable_ipv6=enable_ipv6)
@ddt.data(['ip', 'alice', False], ['ip', '1.1.1.0/10/12', False], @ddt.data(['ip', 'alice', False], ['ip', '1.1.1.0/10/12', False],
['ip', '255.255.255.265', False], ['ip', '1.1.1.0/34', False], ['ip', '255.255.255.265', False], ['ip', '1.1.1.0/34', False],
['cert', '', False], ['cephx', 'client.alice', True], ['cert', '', False], ['cephx', 'client.alice', True],
['group', 'alice', True], ['cephx', 'alice', False], ['group', 'alice', True], ['cephx', 'alice', False],
['cephx', '', True], ['user', 'bob', False]) ['cephx', '', True], ['user', 'bob', False],
['ip', '1001::1001/256', False],
['ip', '1001:1001/256', False],)
@ddt.unpack @ddt.unpack
def test_validate_access_exception(self, access_type, access_to, ceph): def test_validate_access_exception(self, access_type, access_to, ceph):
self.assertRaises(webob.exc.HTTPBadRequest, common.validate_access, self.assertRaises(webob.exc.HTTPBadRequest, common.validate_access,

View File

@ -364,7 +364,11 @@ class ShareSnapshotAPITest(test.TestCase):
self.assertEqual(expected, actual['snapshot_access_list']) self.assertEqual(expected, actual['snapshot_access_list'])
def test_allow_access(self): @ddt.data(('1.1.1.1', '2.32'),
('1.1.1.1', '2.38'),
('1001::1001', '2.38'))
@ddt.unpack
def test_allow_access(self, ip_address, version):
share = db_utils.create_share(mount_snapshot_support=True) share = db_utils.create_share(mount_snapshot_support=True)
snapshot = db_utils.create_snapshot( snapshot = db_utils.create_snapshot(
status=constants.STATUS_AVAILABLE, share_id=share['id']) status=constants.STATUS_AVAILABLE, share_id=share['id'])
@ -372,7 +376,7 @@ class ShareSnapshotAPITest(test.TestCase):
access = { access = {
'id': 'fake_id', 'id': 'fake_id',
'access_type': 'ip', 'access_type': 'ip',
'access_to': '1.1.1.1', 'access_to': ip_address,
'state': 'new', 'state': 'new',
} }
@ -384,7 +388,7 @@ class ShareSnapshotAPITest(test.TestCase):
mock.Mock(return_value=access)) mock.Mock(return_value=access))
body = {'allow_access': access} body = {'allow_access': access}
req = fakes.HTTPRequest.blank('/snapshots/%s/action' % snapshot['id'], req = fakes.HTTPRequest.blank('/snapshots/%s/action' % snapshot['id'],
version='2.32') version=version)
actual = self.controller.allow_access(req, snapshot['id'], body) actual = self.controller.allow_access(req, snapshot['id'], body)

View File

@ -1892,17 +1892,37 @@ class ShareActionsTest(test.TestCase):
self.controller = shares.ShareController() self.controller = shares.ShareController()
self.mock_object(share_api.API, 'get', stubs.stub_share_get) self.mock_object(share_api.API, 'get', stubs.stub_share_get)
@ddt.unpack
@ddt.data( @ddt.data(
{'access_type': 'ip', 'access_to': '127.0.0.1'}, {"access": {'access_type': 'ip', 'access_to': '127.0.0.1'},
{'access_type': 'user', 'access_to': '1' * 4}, "version": "2.7"},
{'access_type': 'user', 'access_to': '1' * 255}, {"access": {'access_type': 'user', 'access_to': '1' * 4},
{'access_type': 'user', 'access_to': 'fake\\]{.-_\'`;}['}, "version": "2.7"},
{'access_type': 'user', 'access_to': 'MYDOMAIN\\Administrator'}, {"access": {'access_type': 'user', 'access_to': '1' * 255},
{'access_type': 'cert', 'access_to': 'x'}, "version": "2.7"},
{'access_type': 'cert', 'access_to': 'tenant.example.com'}, {"access": {'access_type': 'user', 'access_to': 'fake\\]{.-_\'`;}['},
{'access_type': 'cert', 'access_to': 'x' * 64}, "version": "2.7"},
{"access": {'access_type': 'user',
'access_to': 'MYDOMAIN\\Administrator'},
"version": "2.7"},
{"access": {'access_type': 'cert', 'access_to': 'x'},
"version": "2.7"},
{"access": {'access_type': 'cert', 'access_to': 'tenant.example.com'},
"version": "2.7"},
{"access": {'access_type': 'cert', 'access_to': 'x' * 64},
"version": "2.7"},
{"access": {'access_type': 'ip', 'access_to': 'ad80::abaa:0:c2:2'},
"version": "2.38"},
{"access": {'access_type': 'ip', 'access_to': 'AD80:ABAA::'},
"version": "2.38"},
{"access": {'access_type': 'ip', 'access_to': 'AD80::/36'},
"version": "2.38"},
{"access": {'access_type': 'ip', 'access_to': 'AD80:ABAA::/128'},
"version": "2.38"},
{"access": {'access_type': 'ip', 'access_to': '127.0.0.1'},
"version": "2.38"},
) )
def test_allow_access(self, access): def test_allow_access(self, access, version):
self.mock_object(share_api.API, self.mock_object(share_api.API,
'allow_access', 'allow_access',
mock.Mock(return_value={'fake': 'fake'})) mock.Mock(return_value={'fake': 'fake'}))
@ -1913,31 +1933,55 @@ class ShareActionsTest(test.TestCase):
body = {'allow_access': access} body = {'allow_access': access}
expected = {'access': {'fake': 'fake'}} expected = {'access': {'fake': 'fake'}}
req = fakes.HTTPRequest.blank( req = fakes.HTTPRequest.blank(
'/v2/tenant1/shares/%s/action' % id, version="2.7") '/v2/tenant1/shares/%s/action' % id, version=version)
res = self.controller.allow_access(req, id, body) res = self.controller.allow_access(req, id, body)
self.assertEqual(expected, res) self.assertEqual(expected, res)
@ddt.unpack
@ddt.data( @ddt.data(
{'access_type': 'error_type', 'access_to': '127.0.0.1'}, {"access": {'access_type': 'error_type',
{'access_type': 'ip', 'access_to': 'localhost'}, 'access_to': '127.0.0.1'},
{'access_type': 'ip', 'access_to': '127.0.0.*'}, "version": "2.7"},
{'access_type': 'ip', 'access_to': '127.0.0.0/33'}, {"access": {'access_type': 'ip', 'access_to': 'localhost'},
{'access_type': 'ip', 'access_to': '127.0.0.256'}, "version": "2.7"},
{'access_type': 'user', 'access_to': '1'}, {"access": {'access_type': 'ip', 'access_to': '127.0.0.*'},
{'access_type': 'user', 'access_to': '1' * 3}, "version": "2.7"},
{'access_type': 'user', 'access_to': '1' * 256}, {"access": {'access_type': 'ip', 'access_to': '127.0.0.0/33'},
{'access_type': 'user', 'access_to': 'root^'}, "version": "2.7"},
{'access_type': 'cert', 'access_to': ''}, {"access": {'access_type': 'ip', 'access_to': '127.0.0.256'},
{'access_type': 'cert', 'access_to': ' '}, "version": "2.7"},
{'access_type': 'cert', 'access_to': 'x' * 65}, {"access": {'access_type': 'user', 'access_to': '1'},
"version": "2.7"},
{"access": {'access_type': 'user', 'access_to': '1' * 3},
"version": "2.7"},
{"access": {'access_type': 'user', 'access_to': '1' * 256},
"version": "2.7"},
{"access": {'access_type': 'user', 'access_to': 'root^'},
"version": "2.7"},
{"access": {'access_type': 'cert', 'access_to': ''},
"version": "2.7"},
{"access": {'access_type': 'cert', 'access_to': ' '},
"version": "2.7"},
{"access": {'access_type': 'cert', 'access_to': 'x' * 65},
"version": "2.7"},
{"access": {'access_type': 'ip', 'access_to': 'ad80::abaa:0:c2:2'},
"version": "2.37"},
{"access": {'access_type': 'ip', 'access_to': '127.4.0.3/33'},
"version": "2.38"},
{"access": {'access_type': 'ip', 'access_to': 'AD80:ABAA::*'},
"version": "2.38"},
{"access": {'access_type': 'ip', 'access_to': 'AD80::/129'},
"version": "2.38"},
{"access": {'access_type': 'ip', 'access_to': 'ad80::abaa:0:c2:2/64'},
"version": "2.38"},
) )
def test_allow_access_error(self, access): def test_allow_access_error(self, access, version):
id = 'fake_share_id' id = 'fake_share_id'
body = {'allow_access': access} body = {'allow_access': access}
req = fakes.HTTPRequest.blank('/v2/tenant1/shares/%s/action' % id, req = fakes.HTTPRequest.blank('/v2/tenant1/shares/%s/action' % id,
version="2.7") version=version)
self.assertRaises(webob.exc.HTTPBadRequest, self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.allow_access, req, id, body) self.controller.allow_access, req, id, body)

View File

@ -30,7 +30,7 @@ ShareGroup = [
help="The minimum api microversion is configured to be the " help="The minimum api microversion is configured to be the "
"value of the minimum microversion supported by Manila."), "value of the minimum microversion supported by Manila."),
cfg.StrOpt("max_api_microversion", cfg.StrOpt("max_api_microversion",
default="2.37", default="2.38",
help="The maximum api microversion is configured to be the " help="The maximum api microversion is configured to be the "
"value of the latest microversion supported by Manila."), "value of the latest microversion supported by Manila."),
cfg.StrOpt("region", cfg.StrOpt("region",

View File

@ -13,9 +13,9 @@
# 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 itertools
import ddt import ddt
import itertools
from tempest import config from tempest import config
from tempest.lib import exceptions as lib_exc from tempest.lib import exceptions as lib_exc
import testtools import testtools
@ -92,11 +92,15 @@ class ShareIpRulesForNFSTest(base.BaseSharesTest):
cls.access_to = "2.2.2.2" cls.access_to = "2.2.2.2"
@tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND) @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
@ddt.data(*set(['1.0', '2.9', '2.27', '2.28', LATEST_MICROVERSION])) @ddt.data(*itertools.chain(
def test_create_delete_access_rules_with_one_ip(self, version): itertools.product({'1.0', '2.9', '2.37', LATEST_MICROVERSION},
{utils.rand_ip()}),
# test data itertools.product({'2.37', LATEST_MICROVERSION},
access_to = "1.1.1.1" {utils.rand_ipv6_ip()})
))
@ddt.unpack
def test_create_delete_access_rules_with_one_ip(self, version,
access_to):
# create rule # create rule
if utils.is_microversion_eq(version, '1.0'): if utils.is_microversion_eq(version, '1.0'):
@ -140,11 +144,14 @@ class ShareIpRulesForNFSTest(base.BaseSharesTest):
rule_id=rule["id"], share_id=self.share['id'], version=version) rule_id=rule["id"], share_id=self.share['id'], version=version)
@tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND) @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
@ddt.data(*set(['1.0', '2.9', '2.27', '2.28', LATEST_MICROVERSION])) @ddt.data(*itertools.chain(
def test_create_delete_access_rule_with_cidr(self, version): itertools.product({'1.0', '2.9', '2.37', LATEST_MICROVERSION},
{utils.rand_ip(network=True)}),
# test data itertools.product({'2.37', LATEST_MICROVERSION},
access_to = "1.2.3.4/32" {utils.rand_ipv6_ip(network=True)})
))
@ddt.unpack
def test_create_delete_access_rule_with_cidr(self, version, access_to):
# create rule # create rule
if utils.is_microversion_eq(version, '1.0'): if utils.is_microversion_eq(version, '1.0'):

View File

@ -46,60 +46,23 @@ class ShareIpRulesForNFSNegativeTest(base.BaseSharesMixedTest):
cls.snap = cls.create_snapshot_wait_for_active(cls.share["id"]) cls.snap = cls.create_snapshot_wait_for_active(cls.share["id"])
@tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND) @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
@ddt.data('shares_client', 'shares_v2_client') @ddt.data('1.2.3.256',
def test_create_access_rule_ip_with_wrong_target_1(self, client_name): '1.1.1.-',
self.assertRaises(lib_exc.BadRequest, '1.2.3.4/33',
getattr(self, client_name).create_access_rule, '1.2.3.*',
self.share["id"], "ip", "1.2.3.256") '1.2.3.*/23',
'1.2.3.1|23',
@tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND) '1.2.3.1/-1',
@ddt.data('shares_client', 'shares_v2_client') '1.2.3.1/',
def test_create_access_rule_ip_with_wrong_target_2(self, client_name): 'ad80::abaa:0:c2:2/-3',
self.assertRaises(lib_exc.BadRequest, 'AD80:ABAA::|26',
getattr(self, client_name).create_access_rule, '2001:DB8:2de:0:0:0:0:e13:200a',
self.share["id"], "ip", "1.1.1.-") )
def test_create_access_rule_ip_with_wrong_target(self, ip_address):
@tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND) for client_name in ['shares_client', 'shares_v2_client']:
@ddt.data('shares_client', 'shares_v2_client') self.assertRaises(lib_exc.BadRequest,
def test_create_access_rule_ip_with_wrong_target_3(self, client_name): getattr(self, client_name).create_access_rule,
self.assertRaises(lib_exc.BadRequest, self.share["id"], "ip", ip_address)
getattr(self, client_name).create_access_rule,
self.share["id"], "ip", "1.2.3.4/33")
@tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
@ddt.data('shares_client', 'shares_v2_client')
def test_create_access_rule_ip_with_wrong_target_4(self, client_name):
self.assertRaises(lib_exc.BadRequest,
getattr(self, client_name).create_access_rule,
self.share["id"], "ip", "1.2.3.*")
@tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
@ddt.data('shares_client', 'shares_v2_client')
def test_create_access_rule_ip_with_wrong_target_5(self, client_name):
self.assertRaises(lib_exc.BadRequest,
getattr(self, client_name).create_access_rule,
self.share["id"], "ip", "1.2.3.*/23")
@tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
@ddt.data('shares_client', 'shares_v2_client')
def test_create_access_rule_ip_with_wrong_target_6(self, client_name):
self.assertRaises(lib_exc.BadRequest,
getattr(self, client_name).create_access_rule,
self.share["id"], "ip", "1.2.3.1|23")
@tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
@ddt.data('shares_client', 'shares_v2_client')
def test_create_access_rule_ip_with_wrong_target_7(self, client_name):
self.assertRaises(lib_exc.BadRequest,
getattr(self, client_name).create_access_rule,
self.share["id"], "ip", "1.2.3.1/-1")
@tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
@ddt.data('shares_client', 'shares_v2_client')
def test_create_access_rule_ip_with_wrong_target_8(self, client_name):
self.assertRaises(lib_exc.BadRequest,
getattr(self, client_name).create_access_rule,
self.share["id"], "ip", "1.2.3.1/")
@tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND) @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
@ddt.data('shares_client', 'shares_v2_client') @ddt.data('shares_client', 'shares_v2_client')

View File

@ -48,10 +48,10 @@ class SnapshotIpRulesForNFSNegativeTest(
@tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND) @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
@ddt.data("1.2.3.256", "1.1.1.-", "1.2.3.4/33", "1.2.3.*", "1.2.3.*/23", @ddt.data("1.2.3.256", "1.1.1.-", "1.2.3.4/33", "1.2.3.*", "1.2.3.*/23",
"1.2.3.1|23", "1.2.3.1/", "1.2.3.1/-1", "fe00::1", "1.2.3.1|23", "1.2.3.1/", "1.2.3.1/-1",
"fe80::217:f2ff:fe07:ed62", "2001:db8::/48", "::1/128", "fe80:217:f2ff:fe07:ed62", "2001:db8::1/148",
"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
"2001:0db8:0000:85a3:0000:0000:ac1f:8001") "2001:0db8:0000:85a3:0000:0000:ac1f:8001/64")
def test_create_access_rule_ip_with_wrong_target(self, target): def test_create_access_rule_ip_with_wrong_target(self, target):
self.assertRaises(lib_exc.BadRequest, self.assertRaises(lib_exc.BadRequest,
self.shares_v2_client.create_snapshot_access_rule, self.shares_v2_client.create_snapshot_access_rule,

View File

@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from netaddr import ip
import random import random
import re import re
@ -90,16 +91,33 @@ def skip_if_microversion_lt(microversion):
return lambda f: f return lambda f: f
def rand_ip(): def rand_ip(network=False):
"""This uses the TEST-NET-3 range of reserved IP addresses. """This uses the TEST-NET-3 range of reserved IP addresses.
Using this range, which are reserved solely for use in Using this range, which are reserved solely for use in
documentation and example source code, should avoid any potential documentation and example source code, should avoid any potential
conflicts in real-world testing. conflicts in real-world testing.
""" """
TEST_NET_3 = '203.0.113.' test_net_3 = '203.0.113.'
final_octet = six.text_type(random.randint(0, 255)) address = test_net_3 + six.text_type(random.randint(0, 255))
return TEST_NET_3 + final_octet if network:
mask_length = six.text_type(random.randint(24, 32))
address = '/'.join((address, mask_length))
ip_network = ip.IPNetwork(address)
return '/'.join((six.text_type(ip_network.network), mask_length))
return address
def rand_ipv6_ip(network=False):
"""This uses the IPv6 documentation range of 2001:DB8::/32"""
ran_add = ["%x" % random.randrange(0, 16**4) for i in range(6)]
address = "2001:0DB8:" + ":".join(ran_add)
if network:
mask_length = six.text_type(random.randint(32, 128))
address = '/'.join((address, mask_length))
ip_network = ip.IPNetwork(address)
return '/'.join((six.text_type(ip_network.network), mask_length))
return address
def choose_matching_backend(share, pools, share_type): def choose_matching_backend(share, pools, share_type):

View File

@ -0,0 +1,4 @@
---
features:
- Validation of IPv6 based addresses has been added
for allow access API when access type is IP.