Determine ip version during subnet create.

Currently, the default IP version for a subnet-create is IPv4.
This behavior is wrong when we use a IPv6 subnetpool and
do not specify the ip version explicitly during the subnet
create operation.

This fix proposes to make the neutron client code for subnet
creation become aware of the subnetpool version. This ensures
that the proper IP version is sent down to the neutron server
if the subnet create is requested with a subnetpool.

Co-Authored-By: Akihiro Motoki <amotoki@gmail.com>

Change-Id: I13da0f204e8ce335a2082c7d829bee28ac567c3f
Closes-bug: 1444146
This commit is contained in:
Sudipta Biswas 2015-07-04 15:56:59 +09:00 committed by Akihiro Motoki
parent 52721a8e93
commit 043656c8f8
3 changed files with 94 additions and 28 deletions

View File

@ -46,8 +46,8 @@ def _get_resource_plural(resource, client):
return resource + 's'
def find_resourceid_by_id(client, resource, resource_id, cmd_resource=None,
parent_id=None):
def find_resource_by_id(client, resource, resource_id, cmd_resource=None,
parent_id=None, fields=None):
if not cmd_resource:
cmd_resource = resource
cmd_resource_plural = _get_resource_plural(cmd_resource, client)
@ -57,12 +57,15 @@ def find_resourceid_by_id(client, resource, resource_id, cmd_resource=None,
match = re.match(UUID_PATTERN, resource_id)
collection = resource_plural
if match:
params = {'id': resource_id}
if fields:
params['fields'] = fields
if parent_id:
data = obj_lister(parent_id, id=resource_id, fields='id')
data = obj_lister(parent_id, **params)
else:
data = obj_lister(id=resource_id, fields='id')
data = obj_lister(**params)
if data and data[collection]:
return data[collection][0]['id']
return data[collection][0]
not_found_message = (_("Unable to find %(resource)s with id "
"'%(id)s'") %
{'resource': resource, 'id': resource_id})
@ -71,14 +74,23 @@ def find_resourceid_by_id(client, resource, resource_id, cmd_resource=None,
message=not_found_message, status_code=404)
def _find_resourceid_by_name(client, resource, name, project_id=None,
cmd_resource=None, parent_id=None):
def find_resourceid_by_id(client, resource, resource_id, cmd_resource=None,
parent_id=None):
info = find_resource_by_id(client, resource, resource_id, cmd_resource,
parent_id, fields='id')
return info['id']
def _find_resource_by_name(client, resource, name, project_id=None,
cmd_resource=None, parent_id=None, fields=None):
if not cmd_resource:
cmd_resource = resource
cmd_resource_plural = _get_resource_plural(cmd_resource, client)
resource_plural = _get_resource_plural(resource, client)
obj_lister = getattr(client, "list_%s" % cmd_resource_plural)
params = {'name': name, 'fields': 'id'}
params = {'name': name}
if fields:
params['fields'] = fields
if project_id:
params['tenant_id'] = project_id
if parent_id:
@ -98,18 +110,27 @@ def _find_resourceid_by_name(client, resource, name, project_id=None,
raise exceptions.NeutronClientException(
message=not_found_message, status_code=404)
else:
return info[0]['id']
return info[0]
def find_resource_by_name_or_id(client, resource, name_or_id,
project_id=None, cmd_resource=None,
parent_id=None, fields=None):
try:
return find_resource_by_id(client, resource, name_or_id,
cmd_resource, parent_id, fields)
except exceptions.NeutronClientException:
return _find_resource_by_name(client, resource, name_or_id,
project_id, cmd_resource, parent_id,
fields)
def find_resourceid_by_name_or_id(client, resource, name_or_id,
project_id=None, cmd_resource=None,
parent_id=None):
try:
return find_resourceid_by_id(client, resource, name_or_id,
cmd_resource, parent_id)
except exceptions.NeutronClientException:
return _find_resourceid_by_name(client, resource, name_or_id,
project_id, cmd_resource, parent_id)
return find_resource_by_name_or_id(client, resource, name_or_id,
project_id, cmd_resource,
parent_id, fields='id')['id']
def add_show_list_common_argument(parser):

View File

@ -165,7 +165,10 @@ class CreateSubnet(neutronV20.CreateCommand):
'--ip-version',
type=int,
default=4, choices=[4, 6],
help=_('IP version to use, default is 4.'))
help=_('IP version to use, default is 4. '
'Note that when subnetpool is specified, '
'IP version is determined from the subnetpool '
'and this option is ignored.'))
parser.add_argument(
'--ip_version',
type=int,
@ -196,8 +199,25 @@ class CreateSubnet(neutronV20.CreateCommand):
def args2body(self, parsed_args):
_network_id = neutronV20.find_resourceid_by_name_or_id(
self.get_client(), 'network', parsed_args.network_id)
body = {'subnet': {'network_id': _network_id,
'ip_version': parsed_args.ip_version, }, }
body = {'subnet': {'network_id': _network_id}}
if parsed_args.prefixlen:
body['subnet'].update({'prefixlen': parsed_args.prefixlen})
if parsed_args.subnetpool:
if parsed_args.subnetpool == 'None':
_subnetpool_id = None
else:
_subnetpool = neutronV20.find_resource_by_name_or_id(
self.get_client(), 'subnetpool', parsed_args.subnetpool)
_subnetpool_id = _subnetpool['id']
# Now that we have the pool_id - let's just have a check on the
# ip version used in the pool
parsed_args.ip_version = _subnetpool['ip_version']
body['subnet'].update({'subnetpool_id': _subnetpool_id})
# IP version needs to be set as IP version can be
# determined by subnetpool.
body['subnet']['ip_version'] = parsed_args.ip_version
if parsed_args.cidr:
# With subnetpool, cidr is now optional for creating subnet.
@ -212,16 +232,6 @@ class CreateSubnet(neutronV20.CreateCommand):
% {"ip": parsed_args.ip_version,
"cidr": unusable_cidr})
if parsed_args.prefixlen:
body['subnet'].update({'prefixlen': parsed_args.prefixlen})
if parsed_args.subnetpool:
if parsed_args.subnetpool == 'None':
_subnetpool_id = None
else:
_subnetpool_id = neutronV20.find_resourceid_by_name_or_id(
self.get_client(), 'subnetpool', parsed_args.subnetpool)
body['subnet'].update({'subnetpool_id': _subnetpool_id})
updatable_args2body(parsed_args, body)
if parsed_args.tenant_id:
body['subnet'].update({'tenant_id': parsed_args.tenant_id})

View File

@ -19,6 +19,7 @@ import sys
from mox3 import mox
from neutronclient.common import exceptions
from neutronclient.neutron import v2_0 as neutronV20
from neutronclient.neutron.v2_0 import subnet
from neutronclient.tests.unit import test_cli20
@ -484,6 +485,28 @@ class CLITestV20SubnetJSON(test_cli20.CLITestV20Base):
position_values, tenant_id='tenantid',
no_api_call=True, expected_exception=exceptions.CommandError)
def test_create_subnet_with_subnetpool_ipv6_and_ip_ver_ignored(self):
resource = 'subnet'
cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None)
name = 'myname'
myid = 'myid'
netid = 'netid'
args = ['--tenant_id', 'tenantid',
'--ip-version', '4',
'--subnetpool', 'subnetpool_id',
netid]
position_names = ['ip_version', 'network_id', 'subnetpool_id']
position_values = [6, netid, 'subnetpool_id']
self.mox.StubOutWithMock(neutronV20, 'find_resource_by_name_or_id')
neutronV20.find_resource_by_name_or_id(
self.client,
'subnetpool',
'subnetpool_id').AndReturn({'id': 'subnetpool_id',
'ip_version': 6})
self._test_create_resource(
resource, cmd, name, myid, args, position_names,
position_values, tenant_id='tenantid')
def test_create_subnet_with_subnetpool_ipv4_with_cidr_wildcard(self):
resource = 'subnet'
cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None)
@ -499,6 +522,12 @@ class CLITestV20SubnetJSON(test_cli20.CLITestV20Base):
position_names = ['ip_version', 'ipv6_address_mode',
'network_id', 'subnetpool_id', 'cidr']
position_values = [4, None, netid, 'subnetpool_id', cidr]
self.mox.StubOutWithMock(neutronV20, 'find_resource_by_name_or_id')
neutronV20.find_resource_by_name_or_id(
self.client,
'subnetpool',
'subnetpool_id').AndReturn({'id': 'subnetpool_id',
'ip_version': 4})
self._test_create_resource(
resource, cmd, name, myid, args, position_names,
position_values, tenant_id='tenantid',
@ -519,6 +548,12 @@ class CLITestV20SubnetJSON(test_cli20.CLITestV20Base):
position_names = ['ip_version', 'ipv6_address_mode',
'network_id', 'subnetpool_id']
position_values = [4, None, netid, 'subnetpool_id']
self.mox.StubOutWithMock(neutronV20, 'find_resource_by_name_or_id')
neutronV20.find_resource_by_name_or_id(
self.client,
'subnetpool',
'subnetpool_id').AndReturn({'id': 'subnetpool_id',
'ip_version': 4})
self._test_create_resource(
resource, cmd, name, myid, args, position_names,
position_values, tenant_id='tenantid',