Add segment property for subnet

Change-Id: I662e47c618ece613d4cc9b2a2bd340c0cf64adbb
Partially-Implements: blueprint neutron-routed-networks
This commit is contained in:
rabi 2017-03-31 10:24:09 +05:30
parent 889c1c774e
commit a466f2a56f
5 changed files with 104 additions and 2 deletions

View File

@ -16,6 +16,9 @@ from openstack import exceptions
from openstack import profile
from heat.engine.clients import client_plugin
from heat.engine import constraints
CLIENT_NAME = 'openstack'
class OpenStackSDKPlugin(client_plugin.ClientPlugin):
@ -43,3 +46,16 @@ class OpenStackSDKPlugin(client_plugin.ClientPlugin):
def is_not_found(self, ex):
return isinstance(ex, exceptions.ResourceNotFound)
def find_network_segment(self, value):
return self.client().network.find_segment(value).id
class SegmentConstraint(constraints.BaseCustomConstraint):
expected_exceptions = (exceptions.ResourceNotFound,
exceptions.DuplicateResource)
def validate_with_client(self, client, value):
sdk_plugin = client.client_plugin(CLIENT_NAME)
sdk_plugin.find_network_segment(value)

View File

@ -38,12 +38,12 @@ class Subnet(neutron.NeutronResource):
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,
IPV6_RA_MODE, IPV6_ADDRESS_MODE, SEGMENT
) = (
'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',
'ipv6_ra_mode', 'ipv6_address_mode', 'segment'
)
_ALLOCATION_POOL_KEYS = (
@ -236,6 +236,14 @@ class Subnet(neutron.NeutronResource):
],
support_status=support.SupportStatus(version='2015.1')
),
SEGMENT: properties.Schema(
properties.Schema.STRING,
_('The name/ID of the segment to associate.'),
constraints=[
constraints.CustomConstraint('neutron.segment')
],
support_status=support.SupportStatus(version='9.0.0')
),
}
attributes_schema = {
@ -303,6 +311,13 @@ class Subnet(neutron.NeutronResource):
client_plugin=self.client_plugin(),
finder='find_resourceid_by_name_or_id',
entity='subnetpool'
),
translation.TranslationRule(
props,
translation.TranslationRule.RESOLVE,
[self.SEGMENT],
client_plugin=self.client_plugin('openstack'),
finder='find_network_segment'
)
]
@ -357,6 +372,8 @@ class Subnet(neutron.NeutronResource):
self.properties,
self.physical_resource_name())
props['network_id'] = props.pop(self.NETWORK)
if self.SEGMENT in props and props[self.SEGMENT]:
props['segment_id'] = props.pop(self.SEGMENT)
if self.SUBNETPOOL in props and props[self.SUBNETPOOL]:
props['subnetpool_id'] = props.pop('subnetpool')
self._null_gateway_ip(props)

View File

@ -11,6 +11,11 @@
# License for the specific language governing permissions and limitations
# under the License.
import mock
from openstack import exceptions
from heat.engine.clients.os import openstacksdk
from heat.tests import common
from heat.tests import utils
@ -22,3 +27,22 @@ class OpenStackSDKPluginTest(common.HeatTestCase):
plugin = context.clients.client_plugin('openstack')
client = plugin.client()
self.assertIsNotNone(client.network.segments)
class SegmentConstraintTest(common.HeatTestCase):
def setUp(self):
super(SegmentConstraintTest, self).setUp()
self.ctx = utils.dummy_context()
self.mock_find_segment = mock.Mock()
self.ctx.clients.client_plugin(
'openstack').find_network_segment = self.mock_find_segment
self.constraint = openstacksdk.SegmentConstraint()
def test_validation(self):
self.mock_find_segment.side_effect = [
"seg1", exceptions.ResourceNotFound(),
exceptions.DuplicateResource()]
self.assertTrue(self.constraint.validate("foo", self.ctx))
self.assertFalse(self.constraint.validate("bar", self.ctx))
self.assertFalse(self.constraint.validate("baz", self.ctx))

View File

@ -20,6 +20,7 @@ import six
from heat.common import exception
from heat.common import template_format
from heat.engine.clients.os import neutron
from heat.engine.clients.os import openstacksdk
from heat.engine.hot import functions as hot_funcs
from heat.engine.resources.openstack.neutron import subnet
from heat.engine import rsrc_defn
@ -111,6 +112,9 @@ class NeutronSubnetTest(common.HeatTestCase):
self.patchobject(neutron.NeutronClientPlugin, 'has_extension',
return_value=True)
self.patchobject(openstacksdk.OpenStackSDKPlugin,
'find_network_segment',
return_value='fc68ea2c-b60b-4b4f-bd82-94ec81110766')
self.patchobject(neutronV20, 'find_resourceid_by_name_or_id',
return_value='fc68ea2c-b60b-4b4f-bd82-94ec81110766')
@ -333,6 +337,46 @@ class NeutronSubnetTest(common.HeatTestCase):
self.assertEqual('91e47a57-7508-46fe-afc9-fc454e8580e1', ref_id)
scheduler.TaskRunner(rsrc.delete)()
def test_subnet_with_segment(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"}],
"segment_id": 'fc68ea2c-b60b-4b4f-bd82-94ec81110766',
"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": "fc68ea2c-b60b-4b4f-bd82-94ec81110766",
"tenant_id": "c1210485b2424d48804aad5d39c61b8f"
}
}
self.create_mock.return_value = subnet_dict
self.show_mock.side_effect = [
qe.NeutronClientException(status_code=404)]
t = template_format.parse(neutron_template)
del t['resources']['sub_net']['properties']['cidr']
t['resources']['sub_net']['properties'][
'segment'] = 'fc68ea2c-b60b-4b4f-bd82-94ec81110766'
t['resources']['sub_net']['properties'][
'prefixlen'] = 24
t['resources']['sub_net']['properties'][
'name'] = 'mysubnet'
stack = utils.parse_stack(t)
self.patchobject(stack['net'], 'FnGetRefId',
return_value='fc68ea2c-b60b-4b4f-bd82-94ec81110766')
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)()
def test_subnet_deprecated(self):
t, stack = self._setup_mock(use_deprecated_templ=True)
self.patchobject(stack['net'], 'FnGetRefId',

View File

@ -132,6 +132,7 @@ heat.constraints =
neutron.qos_policy = heat.engine.clients.os.neutron.neutron_constraints:QoSPolicyConstraint
neutron.router = heat.engine.clients.os.neutron.neutron_constraints:RouterConstraint
neutron.security_group = heat.engine.clients.os.neutron.neutron_constraints:SecurityGroupConstraint
neutron.segment = heat.engine.clients.os.openstacksdk:SegmentConstraint
neutron.subnet = heat.engine.clients.os.neutron.neutron_constraints:SubnetConstraint
neutron.subnetpool = heat.engine.clients.os.neutron.neutron_constraints:SubnetPoolConstraint
nova.flavor = heat.engine.clients.os.nova:FlavorConstraint