Merge "Add subnetpool property to subnet resource"
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from neutronclient.neutron import v2_0 as neutronV20
|
||||
from oslo_utils import netutils
|
||||
|
||||
from heat.common import exception
|
||||
@@ -25,13 +26,15 @@ from heat.engine import support
|
||||
class Subnet(neutron.NeutronResource):
|
||||
|
||||
PROPERTIES = (
|
||||
NETWORK_ID, NETWORK, CIDR, VALUE_SPECS, NAME, IP_VERSION,
|
||||
DNS_NAMESERVERS, GATEWAY_IP, ENABLE_DHCP, ALLOCATION_POOLS,
|
||||
TENANT_ID, HOST_ROUTES, IPV6_RA_MODE, IPV6_ADDRESS_MODE,
|
||||
NETWORK_ID, NETWORK, SUBNETPOOL, PREFIXLEN, CIDR,
|
||||
VALUE_SPECS, NAME, IP_VERSION, DNS_NAMESERVERS, GATEWAY_IP,
|
||||
ENABLE_DHCP, ALLOCATION_POOLS, TENANT_ID, HOST_ROUTES,
|
||||
IPV6_RA_MODE, IPV6_ADDRESS_MODE,
|
||||
) = (
|
||||
'network_id', 'network', 'cidr', 'value_specs', 'name', 'ip_version',
|
||||
'dns_nameservers', 'gateway_ip', 'enable_dhcp', 'allocation_pools',
|
||||
'tenant_id', 'host_routes', 'ipv6_ra_mode', 'ipv6_address_mode',
|
||||
'network_id', 'network', 'subnetpool', 'prefixlen', 'cidr',
|
||||
'value_specs', 'name', 'ip_version', 'dns_nameservers', 'gateway_ip',
|
||||
'enable_dhcp', 'allocation_pools', 'tenant_id', 'host_routes',
|
||||
'ipv6_ra_mode', 'ipv6_address_mode',
|
||||
)
|
||||
|
||||
_ALLOCATION_POOL_KEYS = (
|
||||
@@ -87,10 +90,23 @@ class Subnet(neutron.NeutronResource):
|
||||
],
|
||||
support_status=support.SupportStatus(version='2014.2')
|
||||
),
|
||||
SUBNETPOOL: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('The name or ID of the subnet pool.'),
|
||||
constraints=[
|
||||
constraints.CustomConstraint('neutron.subnetpool')
|
||||
],
|
||||
support_status=support.SupportStatus(version='6.0.0'),
|
||||
),
|
||||
PREFIXLEN: properties.Schema(
|
||||
properties.Schema.INTEGER,
|
||||
_('Prefix length for subnet allocation from subnet pool.'),
|
||||
constraints=[constraints.Range(min=0)],
|
||||
support_status=support.SupportStatus(version='6.0.0'),
|
||||
),
|
||||
CIDR: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('The CIDR.'),
|
||||
required=True,
|
||||
constraints=[
|
||||
constraints.CustomConstraint('net_cidr')
|
||||
]
|
||||
@@ -272,8 +288,21 @@ class Subnet(neutron.NeutronResource):
|
||||
|
||||
def validate(self):
|
||||
super(Subnet, self).validate()
|
||||
subnetpool = self.properties[self.SUBNETPOOL]
|
||||
prefixlen = self.properties[self.PREFIXLEN]
|
||||
cidr = self.properties[self.CIDR]
|
||||
if subnetpool and cidr:
|
||||
raise exception.ResourcePropertyConflict(self.SUBNETPOOL,
|
||||
self.CIDR)
|
||||
if not subnetpool and not cidr:
|
||||
raise exception.PropertyUnspecifiedError(self.SUBNETPOOL,
|
||||
self.CIDR)
|
||||
if prefixlen and cidr:
|
||||
raise exception.ResourcePropertyConflict(self.PREFIXLEN,
|
||||
self.CIDR)
|
||||
ra_mode = self.properties[self.IPV6_RA_MODE]
|
||||
address_mode = self.properties[self.IPV6_ADDRESS_MODE]
|
||||
|
||||
if (self.properties[self.IP_VERSION] == 4) and (
|
||||
ra_mode or address_mode):
|
||||
msg = _('ipv6_ra_mode and ipv6_address_mode are not supported '
|
||||
@@ -295,9 +324,12 @@ class Subnet(neutron.NeutronResource):
|
||||
props = self.prepare_properties(
|
||||
self.properties,
|
||||
self.physical_resource_name())
|
||||
self.client_plugin().resolve_network(props, self.NETWORK, 'network_id')
|
||||
self.client_plugin().resolve_network(props, self.NETWORK,
|
||||
'network_id')
|
||||
if self.SUBNETPOOL in props and props[self.SUBNETPOOL]:
|
||||
props['subnetpool_id'] = neutronV20.find_resourceid_by_name_or_id(
|
||||
self.client(), 'subnetpool', props.pop('subnetpool'))
|
||||
self._null_gateway_ip(props)
|
||||
|
||||
subnet = self.client().create_subnet({'subnet': props})['subnet']
|
||||
self.resource_id_set(subnet['id'])
|
||||
|
||||
|
||||
@@ -269,6 +269,10 @@ class HeatTestCase(testscenarios.WithScenarios,
|
||||
validate = self.patchobject(neutron.AddressScopeConstraint, 'validate')
|
||||
validate.return_value = True
|
||||
|
||||
def stub_SubnetPoolConstraint_validate(self):
|
||||
validate = self.patchobject(neutron.SubnetPoolConstraint, 'validate')
|
||||
validate.return_value = True
|
||||
|
||||
def stub_RouterConstraint_validate(self):
|
||||
validate = self.patchobject(neutron.RouterConstraint, 'validate')
|
||||
validate.return_value = True
|
||||
|
||||
@@ -179,6 +179,86 @@ class NeutronSubnetTest(common.HeatTestCase):
|
||||
self.assertIsNone(scheduler.TaskRunner(rsrc.delete)())
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_subnet_with_subnetpool(self):
|
||||
subnet_dict = {
|
||||
"subnet": {
|
||||
"allocation_pools": [
|
||||
{"start": "10.0.3.20", "end": "10.0.3.150"}],
|
||||
"host_routes": [
|
||||
{"destination": "10.0.4.0/24", "nexthop": "10.0.3.20"}],
|
||||
"subnetpool_id": "None",
|
||||
"prefixlen": 24,
|
||||
"dns_nameservers": ["8.8.8.8"],
|
||||
"enable_dhcp": True,
|
||||
"gateway_ip": "10.0.3.1",
|
||||
"id": "91e47a57-7508-46fe-afc9-fc454e8580e1",
|
||||
"ip_version": 4,
|
||||
"name": "name",
|
||||
"network_id": "None",
|
||||
"tenant_id": "c1210485b2424d48804aad5d39c61b8f"
|
||||
}
|
||||
}
|
||||
neutronV20.find_resourceid_by_name_or_id(
|
||||
mox.IsA(neutronclient.Client),
|
||||
'subnetpool',
|
||||
'None'
|
||||
).AndReturn('None')
|
||||
neutronV20.find_resourceid_by_name_or_id(
|
||||
mox.IsA(neutronclient.Client),
|
||||
'network',
|
||||
'None'
|
||||
).AndReturn('None')
|
||||
neutronV20.find_resourceid_by_name_or_id(
|
||||
mox.IsA(neutronclient.Client),
|
||||
'subnetpool',
|
||||
'None'
|
||||
).AndReturn('None')
|
||||
neutronclient.Client.create_subnet({
|
||||
'subnet': {
|
||||
'network_id': u'None',
|
||||
'name': 'mysubnet',
|
||||
'subnetpool_id': 'None',
|
||||
'prefixlen': 24,
|
||||
'dns_nameservers': [u'8.8.8.8'],
|
||||
'allocation_pools': [
|
||||
{'start': u'10.0.3.20', 'end': u'10.0.3.150'}],
|
||||
'host_routes': [
|
||||
{'destination': u'10.0.4.0/24', 'nexthop': u'10.0.3.20'}],
|
||||
'ip_version': 4,
|
||||
'enable_dhcp': True,
|
||||
'tenant_id': 'c1210485b2424d48804aad5d39c61b8f'
|
||||
}
|
||||
}).AndReturn(subnet_dict)
|
||||
|
||||
neutronclient.Client.show_subnet(
|
||||
'91e47a57-7508-46fe-afc9-fc454e8580e1').AndReturn(subnet_dict)
|
||||
|
||||
neutronclient.Client.delete_subnet(
|
||||
'91e47a57-7508-46fe-afc9-fc454e8580e1'
|
||||
).AndReturn(None)
|
||||
|
||||
neutronclient.Client.show_subnet(
|
||||
'91e47a57-7508-46fe-afc9-fc454e8580e1'
|
||||
).AndRaise(qe.NeutronClientException(status_code=404))
|
||||
|
||||
self.m.ReplayAll()
|
||||
t = template_format.parse(neutron_template)
|
||||
del t['resources']['sub_net']['properties']['cidr']
|
||||
t['resources']['sub_net']['properties'][
|
||||
'subnetpool'] = 'None'
|
||||
t['resources']['sub_net']['properties'][
|
||||
'prefixlen'] = 24
|
||||
t['resources']['sub_net']['properties'][
|
||||
'name'] = 'mysubnet'
|
||||
stack = utils.parse_stack(t)
|
||||
rsrc = self.create_subnet(t, stack, 'sub_net')
|
||||
scheduler.TaskRunner(rsrc.create)()
|
||||
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
|
||||
ref_id = rsrc.FnGetRefId()
|
||||
self.assertEqual('91e47a57-7508-46fe-afc9-fc454e8580e1', ref_id)
|
||||
scheduler.TaskRunner(rsrc.delete)()
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_subnet_deprecated(self):
|
||||
|
||||
t = self._test_subnet(resolve_neutron=False)
|
||||
@@ -524,6 +604,42 @@ class NeutronSubnetTest(common.HeatTestCase):
|
||||
self.assertEqual("ipv6_ra_mode and ipv6_address_mode are not "
|
||||
"supported for ipv4.", six.text_type(ex))
|
||||
|
||||
def test_validate_both_subnetpool_cidr(self):
|
||||
t = template_format.parse(neutron_template)
|
||||
props = t['resources']['sub_net']['properties']
|
||||
props['subnetpool'] = 'new_pool'
|
||||
stack = utils.parse_stack(t)
|
||||
rsrc = stack['sub_net']
|
||||
ex = self.assertRaises(exception.ResourcePropertyConflict,
|
||||
rsrc.validate)
|
||||
msg = ("Cannot define the following properties at the same time: "
|
||||
"subnetpool, cidr.")
|
||||
self.assertEqual(msg, six.text_type(ex))
|
||||
|
||||
def test_validate_none_subnetpool_cidr(self):
|
||||
t = template_format.parse(neutron_template)
|
||||
props = t['resources']['sub_net']['properties']
|
||||
del props['cidr']
|
||||
stack = utils.parse_stack(t)
|
||||
rsrc = stack['sub_net']
|
||||
ex = self.assertRaises(exception.PropertyUnspecifiedError,
|
||||
rsrc.validate)
|
||||
msg = ("At least one of the following properties must be specified: "
|
||||
"subnetpool, cidr")
|
||||
self.assertEqual(msg, six.text_type(ex))
|
||||
|
||||
def test_validate_both_prefixlen_cidr(self):
|
||||
t = template_format.parse(neutron_template)
|
||||
props = t['resources']['sub_net']['properties']
|
||||
props['prefixlen'] = '24'
|
||||
stack = utils.parse_stack(t)
|
||||
rsrc = stack['sub_net']
|
||||
ex = self.assertRaises(exception.ResourcePropertyConflict,
|
||||
rsrc.validate)
|
||||
msg = ("Cannot define the following properties at the same time: "
|
||||
"prefixlen, cidr.")
|
||||
self.assertEqual(msg, six.text_type(ex))
|
||||
|
||||
def test_deprecated_network_id(self):
|
||||
template = """
|
||||
heat_template_version: 2015-04-30
|
||||
|
||||
Reference in New Issue
Block a user