Support creating network with pool uuid

Right now, we can create network with pool name, but it doesn't
work well if there are two subnetpools with the same name. This
commit add support for pool uuid as an alternative.

Implements: blueprint existing-subnetpool
Closes-Bug: #1659434
Change-Id: Ib7c2bac86a1b1647c2da9684bc4317767116d7aa
This commit is contained in:
Hongbin Lu 2017-01-24 16:42:30 -06:00
parent 9e99cc0242
commit 2e6f4cd1cb
5 changed files with 201 additions and 16 deletions

View File

@ -405,10 +405,11 @@ Limitations
-----------
To create Docker networks with subnets having same/overlapping cidr, it is
expected to pass unique pool name for each such network creation Docker
command. Docker cli options -o and --ipam-opt should be used to pass pool
names as shown below::
expected to pre-create Neutron subnetpool and pass the pool name for each
such network creation Docker command. Docker cli options -o and --ipam-opt
should be used to pass pool names as shown below::
$ neutron subnetpool-create --pool-prefix 10.0.0.0/24 neutron_pool1
$ sudo docker network create --driver=kuryr --ipam-driver=kuryr \
--subnet 10.0.0.0/16 --gateway=10.0.0.1 --ip-range 10.0.0.0/24 \
-o neutron.pool.name=neutron_pool1 \
@ -419,6 +420,7 @@ names as shown below::
Now Docker user creates another network with same cidr as the previous one,
i.e 10.0.0.0/16, but with different pool name, neutron_pool2::
$ neutron subnetpool-create --pool-prefix 10.0.0.0/24 neutron_pool2
$ sudo docker network create --driver=kuryr --ipam-driver=kuryr \
--subnet 10.0.0.0/16 --gateway=10.0.0.1 --ip-range 10.0.0.0/24 \
-o neutron.pool.name=neutron_pool2 \
@ -426,6 +428,16 @@ i.e 10.0.0.0/16, but with different pool name, neutron_pool2::
bar
397badb51ebca09339cb17aaec05e48ffe60659ced6f3fc41b020b0eb506d786
Alternatively, Docker user can pass an existing pool uuid if there are multiple
pools with the same name::
$ sudo sudo docker network create --driver=kuryr --ipam-driver=kuryr \
--subnet 10.0.0.0/16 --gateway=10.0.0.1 --ip-range 10.0.0.0/24 \
-o neutron.pool.uuid=2d5767a4-6c96-4522-ab1d-a06d7adc9e23 \
--ipam-opt=neutron.pool.uuid=2d5767a4-6c96-4522-ab1d-a06d7adc9e23 \
bar
0aed1efbe21f6c29dc77eccd0dd17ba729274f9275070e1469230c864f9054ff
External Resources
------------------

View File

@ -39,5 +39,6 @@ NETWORK_GATEWAY_OPTIONS = 'com.docker.network.gateway'
NETWORK_GENERIC_OPTIONS = 'com.docker.network.generic'
NEUTRON_NAME_OPTION = 'neutron.net.name'
NEUTRON_POOL_NAME_OPTION = 'neutron.pool.name'
NEUTRON_POOL_UUID_OPTION = 'neutron.pool.uuid'
NEUTRON_UUID_OPTION = 'neutron.net.uuid'
REQUEST_ADDRESS_TYPE = 'RequestAddressType'

View File

@ -614,17 +614,21 @@ def network_driver_create_network():
neutron_uuid = generic_options.get(const.NEUTRON_UUID_OPTION)
neutron_name = generic_options.get(const.NEUTRON_NAME_OPTION)
pool_name = generic_options.get(const.NEUTRON_POOL_NAME_OPTION)
pool_id = generic_options.get(const.NEUTRON_POOL_UUID_OPTION)
if not pool_name:
pool_name = lib_utils.get_neutron_subnetpool_name(pool_cidr)
pools = _get_subnetpools_by_attrs(name=pool_name)
if pools:
pool_id = pools[0]['id']
if pool_id:
pools = _get_subnetpools_by_attrs(id=pool_id)
elif pool_name:
pools = _get_subnetpools_by_attrs(name=pool_name)
else:
pool_name = lib_utils.get_neutron_subnetpool_name(pool_cidr)
pools = _get_subnetpools_by_attrs(name=pool_name)
if not pools:
raise exceptions.KuryrException(
("Specified pool name({0}) does not "
"exist.").format(pool_name))
("Specified pool id/name({0}) does not "
"exist.").format(pool_id or pool_name))
pool_id = pools[0]['id']
# let the user override the driver default
if not neutron_uuid and not neutron_name:
@ -1253,13 +1257,14 @@ def ipam_request_pool():
requested_pool = json_data['Pool']
requested_subpool = json_data['SubPool']
v6 = json_data['V6']
pool_id = ''
subnet_cidr = ''
pool_name = ''
pool_id = ''
pools = []
options = json_data.get('Options')
if options:
pool_name = options.get(const.NEUTRON_POOL_NAME_OPTION)
pool_id = options.get(const.NEUTRON_POOL_UUID_OPTION)
if requested_pool:
LOG.info(_LI("Creating subnetpool with the given pool CIDR"))
if requested_subpool:
@ -1272,7 +1277,7 @@ def ipam_request_pool():
LOG.warning(_LW("There is already existing subnet for the "
"same cidr. Please check and specify pool name "
"in Options."))
if not pool_name:
if not pool_name and not pool_id:
pool_name = lib_utils.get_neutron_subnetpool_name(subnet_cidr)
pools = _get_subnetpools_by_attrs(name=pool_name)
if len(pools):
@ -1289,11 +1294,14 @@ def ipam_request_pool():
pool = created_subnetpool_response['subnetpool']
pool_id = pool['id']
else:
existing_pools = _get_subnetpools_by_attrs(name=pool_name)
if pool_id:
existing_pools = _get_subnetpools_by_attrs(id=pool_id)
else:
existing_pools = _get_subnetpools_by_attrs(name=pool_name)
if not existing_pools:
raise exceptions.KuryrException(
("Specified subnetpool name({0}) does not "
"exist.").format(pool_name))
("Specified subnetpool id/name({0}) does not "
"exist.").format(pool_id or pool_name))
pool_id = existing_pools[0]['id']
LOG.info(_LI("Using existing Neutron subnetpool %s successfully"),
pool_id)

View File

@ -519,6 +519,137 @@ class TestKuryr(base.TestKuryrBase):
decoded_json = jsonutils.loads(response.data)
self.assertEqual(constants.SCHEMA['SUCCESS'], decoded_json)
@ddt.data((True), (False))
@mock.patch('kuryr_libnetwork.controllers.app.neutron.list_subnets')
@mock.patch('kuryr_libnetwork.controllers.app.neutron.list_subnetpools')
@mock.patch('kuryr_libnetwork.controllers.app.neutron.create_subnet')
@mock.patch('kuryr_libnetwork.controllers.app.neutron.add_tag')
@mock.patch('kuryr_libnetwork.controllers.app.neutron.create_network')
@mock.patch('kuryr_libnetwork.controllers.app.neutron.list_networks')
@mock.patch(
'kuryr_libnetwork.controllers.app.driver.get_default_network_id')
def test_network_driver_create_network_with_pool_id_option(self,
driver_default_net, mock_get_default_network_id,
mock_list_networks, mock_create_network, mock_add_tag,
mock_create_subnet, mock_list_subnetpools,
mock_list_subnets):
fake_kuryr_subnetpool_id = uuidutils.generate_uuid()
kuryr_subnetpools = self._get_fake_v4_subnetpools(
fake_kuryr_subnetpool_id)
docker_network_id = lib_utils.get_hash()
# The following fake response is retrieved from the Neutron doc:
# http://developer.openstack.org/api-ref-networking-v2.html#createNetwork # noqa
fake_neutron_net_id = "4e8e5957-649f-477b-9e5b-f1f75b21c03c"
driver_value = fake_neutron_net_id if driver_default_net else None
mock_get_default_network_id.return_value = driver_value
fake_network = {
"status": "ACTIVE",
"subnets": [],
"admin_state_up": True,
"tenant_id": "9bacb3c5d39d41a79512987f338cf177",
"router:external": False,
"segments": [],
"shared": False,
"id": fake_neutron_net_id,
}
if driver_value:
fake_existing_networks_response = {
"networks": [fake_network]
}
mock_list_networks.return_value = fake_existing_networks_response
else:
fake_create_network_request = {
"network": {
"name": utils.make_net_name(docker_network_id),
"admin_state_up": True
}
}
fake_network['name'] = utils.make_net_name(docker_network_id)
# The following fake response is retrieved from the Neutron doc:
# http://developer.openstack.org/api-ref-networking-v2.html#createNetwork # noqa
fake_create_network_response = {
"network": fake_network
}
mock_create_network.return_value = fake_create_network_response
tags = utils.create_net_tags(docker_network_id)
fake_existing_subnets_response = {
"subnets": []
}
fake_cidr_v4 = '192.168.42.0/24'
fake_subnet_request = {
"subnets": [{
'name': utils.make_subnet_name(fake_cidr_v4),
'network_id': fake_neutron_net_id,
'ip_version': 4,
'cidr': fake_cidr_v4,
'enable_dhcp': app.enable_dhcp,
'gateway_ip': '192.168.42.1',
'subnetpool_id': fake_kuryr_subnetpool_id,
}]
}
subnet_v4_id = uuidutils.generate_uuid()
fake_v4_subnet = self._get_fake_v4_subnet(
fake_neutron_net_id, subnet_v4_id,
name=fake_cidr_v4, cidr=fake_cidr_v4)
fake_subnet_response = {
'subnets': [
fake_v4_subnet['subnet']
]
}
network_request = {
'NetworkID': docker_network_id,
'IPv4Data': [{
'AddressSpace': 'foo',
'Pool': '192.168.42.0/24',
'Gateway': '192.168.42.1/24',
}],
'IPv6Data': [{
'AddressSpace': 'bar',
'Pool': 'fe80::/64',
'Gateway': 'fe80::f816:3eff:fe20:57c3/64',
}],
'Options': {
'com.docker.network.enable_ipv6': False,
'com.docker.network.generic': {
'neutron.pool.uuid': fake_kuryr_subnetpool_id,
}
}
}
mock_create_subnet.return_value = fake_subnet_response
mock_list_subnetpools.return_value = {'subnetpools':
kuryr_subnetpools['subnetpools']}
mock_list_subnets.return_value = fake_existing_subnets_response
response = self.app.post('/NetworkDriver.CreateNetwork',
content_type='application/json',
data=jsonutils.dumps(network_request))
self.assertEqual(200, response.status_code)
mock_get_default_network_id.assert_any_call()
for tag in tags:
mock_add_tag.assert_any_call('networks',
fake_neutron_net_id, tag)
if driver_value:
mock_list_networks.assert_called_with(id=fake_neutron_net_id)
mock_add_tag.assert_any_call('networks', fake_neutron_net_id,
'kuryr.net.existing')
else:
mock_create_network.assert_called_with(fake_create_network_request)
mock_create_subnet.assert_called_with(fake_subnet_request)
mock_list_subnetpools.assert_called_with(id=fake_kuryr_subnetpool_id)
mock_list_subnets.assert_called_with(network_id=fake_neutron_net_id,
cidr=fake_cidr_v4)
decoded_json = jsonutils.loads(response.data)
self.assertEqual(constants.SCHEMA['SUCCESS'], decoded_json)
@ddt.data((True), (False))
@mock.patch('kuryr_libnetwork.controllers.app.neutron.create_subnet')
@mock.patch('kuryr_libnetwork.controllers.app.neutron.create_network')

View File

@ -148,6 +148,39 @@ class TestKuryrIpam(base.TestKuryrBase):
decoded_json = jsonutils.loads(response.data)
self.assertEqual(fake_kuryr_subnetpool_id, decoded_json['PoolID'])
@mock.patch('kuryr_libnetwork.controllers.app.neutron.list_subnetpools')
@mock.patch('kuryr_libnetwork.controllers.app.neutron.list_subnets')
@ddt.data((FAKE_IP4_CIDR), (FAKE_IP6_CIDR))
def test_ipam_driver_request_pool_with_pool_id_option(self,
pool_cidr, mock_list_subnets, mock_list_subnetpools):
fake_subnet = {"subnets": []}
mock_list_subnets.return_value = fake_subnet
fake_kuryr_subnetpool_id = uuidutils.generate_uuid()
if pool_cidr == FAKE_IP4_CIDR:
kuryr_subnetpools = self._get_fake_v4_subnetpools(
fake_kuryr_subnetpool_id, prefixes=[pool_cidr])
else:
kuryr_subnetpools = self._get_fake_v6_subnetpools(
fake_kuryr_subnetpool_id, prefixes=[pool_cidr])
mock_list_subnetpools.return_value = kuryr_subnetpools
fake_request = {
'AddressSpace': '',
'Pool': pool_cidr,
'SubPool': pool_cidr,
'Options': {'neutron.pool.uuid': fake_kuryr_subnetpool_id},
'V6': False
}
response = self.app.post('/IpamDriver.RequestPool',
content_type='application/json',
data=jsonutils.dumps(fake_request))
self.assertEqual(200, response.status_code)
mock_list_subnets.assert_called_with(cidr=pool_cidr)
decoded_json = jsonutils.loads(response.data)
self.assertEqual(fake_kuryr_subnetpool_id, decoded_json['PoolID'])
@mock.patch('kuryr_libnetwork.controllers.app.neutron.list_subnetpools')
def test_ipam_driver_request_pool_with_default_v6pool(self,
mock_list_subnetpools):