Merge "Add subnetpool property to subnet resource"

This commit is contained in:
Jenkins
2015-12-09 09:21:51 +00:00
committed by Gerrit Code Review
3 changed files with 161 additions and 9 deletions

View File

@@ -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'])

View File

@@ -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

View File

@@ -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