diff --git a/openstack/cloud/_network.py b/openstack/cloud/_network.py index 7fdb63e30..8ac148c35 100644 --- a/openstack/cloud/_network.py +++ b/openstack/cloud/_network.py @@ -457,6 +457,17 @@ class NetworkCloudMixin: """ return self.network.get_port(id) + def get_subnetpool(self, name_or_id): + """Get a subnetpool by name or ID. + + :param name_or_id: Name or ID of the subnetpool. + + :returns: A network ``Subnetpool`` object if found, else None. + """ + return self.network.find_subnet_pool( + name_or_id=name_or_id, ignore_missing=True + ) + def create_network( self, name, @@ -2308,13 +2319,16 @@ class NetworkCloudMixin: ipv6_address_mode=None, prefixlen=None, use_default_subnetpool=False, + subnetpool_name_or_id=None, **kwargs, ): """Create a subnet on a specified network. :param string network_name_or_id: The unique name or ID of the attached network. If a non-unique name is supplied, an exception is raised. - :param string cidr: The CIDR. + :param string cidr: The CIDR. Only one of ``cidr``, + ``use_default_subnetpool`` and ``subnetpool_name_or_id`` may be + specified at the same time. :param int ip_version: The IP version, which is 4 or 6. :param bool enable_dhcp: Set to ``True`` if DHCP is enabled and ``False`` if disabled. Default is ``False``. @@ -2362,10 +2376,15 @@ class NetworkCloudMixin: :param string ipv6_address_mode: IPv6 address mode. Valid values are: 'dhcpv6-stateful', 'dhcpv6-stateless', or 'slaac'. :param string prefixlen: The prefix length to use for subnet allocation - from a subnet pool. + from a subnetpool. :param bool use_default_subnetpool: Use the default subnetpool for - ``ip_version`` to obtain a CIDR. It is required to pass ``None`` to - the ``cidr`` argument when enabling this option. + ``ip_version`` to obtain a CIDR. Only one of ``cidr``, + ``use_default_subnetpool`` and ``subnetpool_name_or_id`` may be + specified at the same time. + :param string subnetpool_name_or_id: The unique name or id of the + subnetpool to obtain a CIDR from. Only one of ``cidr``, + ``use_default_subnetpool`` and ``subnetpool_name_or_id`` may be + specified at the same time. :param kwargs: Key value pairs to be passed to the Neutron API. :returns: The created network ``Subnet`` object. :raises: OpenStackCloudException on operation error. @@ -2387,17 +2406,31 @@ class NetworkCloudMixin: 'arg:disable_gateway_ip is not allowed with arg:gateway_ip' ) - if not cidr and not use_default_subnetpool: + uses_subnetpool = use_default_subnetpool or subnetpool_name_or_id + if not cidr and not uses_subnetpool: raise exc.OpenStackCloudException( 'arg:cidr is required when a subnetpool is not used' ) - if cidr and use_default_subnetpool: + if cidr and uses_subnetpool: raise exc.OpenStackCloudException( - 'arg:cidr must be set to None when use_default_subnetpool == ' - 'True' + 'arg:cidr and subnetpool may not be used at the same time' ) + if use_default_subnetpool and subnetpool_name_or_id: + raise exc.OpenStackCloudException( + 'arg:use_default_subnetpool and arg:subnetpool_id may not be ' + 'used at the same time' + ) + + subnetpool = None + if subnetpool_name_or_id: + subnetpool = self.get_subnetpool(subnetpool_name_or_id) + if not subnetpool: + raise exc.OpenStackCloudException( + "Subnetpool %s not found." % subnetpool_name_or_id + ) + # Be friendly on ip_version and allow strings if isinstance(ip_version, str): try: @@ -2443,6 +2476,8 @@ class NetworkCloudMixin: subnet['prefixlen'] = prefixlen if use_default_subnetpool: subnet['use_default_subnetpool'] = True + if subnetpool: + subnet['subnetpool_id'] = subnetpool["id"] return self.network.create_subnet(**subnet) diff --git a/openstack/tests/unit/cloud/test_subnet.py b/openstack/tests/unit/cloud/test_subnet.py index 13b6447aa..02925d686 100644 --- a/openstack/tests/unit/cloud/test_subnet.py +++ b/openstack/tests/unit/cloud/test_subnet.py @@ -554,6 +554,93 @@ class TestSubnet(base.TestCase): self._compare_subnets(mock_subnet_rep, subnet) self.assert_calls() + def test_create_subnet_from_specific_subnetpool(self): + pool = [{'start': '172.16.0.2', 'end': '172.16.0.15'}] + id = '143296eb-7f47-4755-835c-488123475604' + gateway = '172.16.0.1' + dns = ['8.8.8.8'] + routes = [{"destination": "0.0.0.0/0", "nexthop": "123.456.78.9"}] + mock_subnet_rep = copy.copy(self.mock_subnet_rep) + mock_subnet_rep['allocation_pools'] = pool + mock_subnet_rep['dns_nameservers'] = dns + mock_subnet_rep['host_routes'] = routes + mock_subnet_rep['gateway_ip'] = gateway + mock_subnet_rep['subnetpool_id'] = self.mock_subnetpool_rep['id'] + mock_subnet_rep['cidr'] = self.subnetpool_cidr + mock_subnet_rep['id'] = id + self.register_uris( + [ + dict( + method='GET', + uri=self.get_mock_url( + 'network', + 'public', + append=['v2.0', 'networks', self.network_name], + ), + status_code=404, + ), + dict( + method='GET', + uri=self.get_mock_url( + 'network', + 'public', + append=['v2.0', 'networks'], + qs_elements=['name=%s' % self.network_name], + ), + json={'networks': [self.mock_network_rep]}, + ), + dict( + method='GET', + uri=self.get_mock_url( + 'network', + 'public', + append=[ + 'v2.0', + 'subnetpools', + self.mock_subnetpool_rep['id'], + ], + ), + json={"subnetpool": self.mock_subnetpool_rep}, + ), + dict( + method='POST', + uri=self.get_mock_url( + 'network', 'public', append=['v2.0', 'subnets'] + ), + json={'subnet': mock_subnet_rep}, + validate=dict( + json={ + 'subnet': { + 'enable_dhcp': False, + 'ip_version': 4, + 'network_id': self.mock_network_rep['id'], + 'allocation_pools': pool, + 'dns_nameservers': dns, + 'subnetpool_id': self.mock_subnetpool_rep[ + 'id' + ], + 'prefixlen': self.prefix_length, + 'host_routes': routes, + } + } + ), + ), + ] + ) + subnet = self.cloud.create_subnet( + self.network_name, + allocation_pools=pool, + dns_nameservers=dns, + subnetpool_name_or_id=self.mock_subnetpool_rep['id'], + prefixlen=self.prefix_length, + host_routes=routes, + ) + mock_subnet_rep.update( + {'prefixlen': self.prefix_length, 'use_default_subnetpool': None} + ) + self._compare_subnets(mock_subnet_rep, subnet) + self.assert_calls() + def test_delete_subnet(self): self.register_uris( [ diff --git a/releasenotes/notes/create-subnet-by-subnetpool-eba1129c67ed4d96.yaml b/releasenotes/notes/create-subnet-by-subnetpool-eba1129c67ed4d96.yaml new file mode 100644 index 000000000..bb7127ac6 --- /dev/null +++ b/releasenotes/notes/create-subnet-by-subnetpool-eba1129c67ed4d96.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Added support for specifying the subnetpool to use when creating subnets + (``subnetpool_name_or_id``)