Provides ability to reference existing neutron resources

Additional properties 'network' and 'subnet' are added to
the resources (existing properties 'network_id' and
'subnet_id' are deprecated). 'network'  and 'subnet' can
now carry name or id. Pre-existing neutron resources
can now be referenced in heat templates by their name
and not necessarily by their UUID. This would help porting
templates across deployments.

Change-Id: I644f5d6ee2e5ccabde190bd73eacc71daaf41f4d
Closes-Bug: #1286128
This commit is contained in:
Rabi Mishra 2014-02-28 12:11:00 +05:30
parent 11c99d7b06
commit 4bf2a1fad2
13 changed files with 895 additions and 190 deletions

View File

@ -16,7 +16,7 @@ from testtools import skipIf
from heat.common import template_format
from heat.engine import clients
from heat.engine import resource
from heat.engine.resources.neutron import router
from heat.engine.resources.neutron import neutron
from heat.engine import scheduler
from heat.openstack.common.importutils import try_import
from heat.tests.common import HeatTestCase
@ -60,7 +60,7 @@ neutron_template = '''
@skipIf(neutronclient is None, 'neutronclient unavailable')
class NeutronExtraRouteTest(HeatTestCase):
@skipIf(router.neutronV20 is None, "Missing Neutron v2_0")
@skipIf(neutron.neutronV20 is None, "Missing Neutron v2_0")
def setUp(self):
super(NeutronExtraRouteTest, self).setUp()
self.m.StubOutWithMock(neutronclient.Client, 'show_router')

View File

@ -15,6 +15,7 @@ from heat.engine import clients
from heat.engine import properties
from heat.engine.resources.neutron import neutron
from heat.engine.resources.neutron import router
from heat.engine import support
if clients.neutronclient is not None:
from neutronclient.common.exceptions import NeutronClientException
@ -22,16 +23,25 @@ if clients.neutronclient is not None:
class FloatingIP(neutron.NeutronResource):
PROPERTIES = (
FLOATING_NETWORK_ID, VALUE_SPECS, PORT_ID, FIXED_IP_ADDRESS,
FLOATING_NETWORK_ID, FLOATING_NETWORK,
VALUE_SPECS, PORT_ID, FIXED_IP_ADDRESS,
) = (
'floating_network_id', 'value_specs', 'port_id', 'fixed_ip_address',
'floating_network_id', 'floating_network',
'value_specs', 'port_id', 'fixed_ip_address',
)
properties_schema = {
FLOATING_NETWORK_ID: properties.Schema(
properties.Schema.STRING,
_('ID of network to allocate floating IP from.'),
required=True
support_status=support.SupportStatus(
support.DEPRECATED,
_('Use property %s.') % FLOATING_NETWORK),
required=False
),
FLOATING_NETWORK: properties.Schema(
properties.Schema.STRING,
_('Network to allocate floating IP from.'),
required=False
),
VALUE_SPECS: properties.Schema(
properties.Schema.MAP,
@ -69,15 +79,28 @@ class FloatingIP(neutron.NeutronResource):
# depend on any RouterGateway in this template with the same
# network_id as this floating_network_id
for resource in self.stack.itervalues():
if (resource.has_interface('OS::Neutron::RouterGateway') and
resource.properties.get(router.RouterGateway.NETWORK_ID) ==
self.properties.get(self.FLOATING_NETWORK_ID)):
deps += (self, resource)
if resource.has_interface('OS::Neutron::RouterGateway'):
gateway_network = resource.properties.get(
router.RouterGateway.NETWORK) or resource.properties.get(
router.RouterGateway.NETWORK_ID)
floating_network = self.properties.get(
self.FLOATING_NETWORK) or self.properties.get(
self.FLOATING_NETWORK_ID)
if gateway_network == floating_network:
deps += (self, resource)
def validate(self):
super(FloatingIP, self).validate()
self._validate_depr_property_required(
self.properties, self.FLOATING_NETWORK, self.FLOATING_NETWORK_ID)
def handle_create(self):
props = self.prepare_properties(
self.properties,
self.physical_resource_name())
self._resolve_network(
self.neutron(), props, self.FLOATING_NETWORK,
'floating_network_id')
fip = self.neutron().create_floatingip({
'floatingip': props})['floatingip']
self.resource_id_set(fip['id'])

View File

@ -19,10 +19,10 @@ from heat.engine import resource
from heat.engine.resources.neutron import neutron
from heat.engine.resources import nova_utils
from heat.engine import scheduler
from heat.engine import support
if clients.neutronclient is not None:
from neutronclient.common.exceptions import NeutronClientException
from neutronclient.neutron import v2_0 as neutronV20
class HealthMonitor(neutron.NeutronResource):
@ -148,10 +148,10 @@ class Pool(neutron.NeutronResource):
"""
PROPERTIES = (
PROTOCOL, SUBNET_ID, LB_METHOD, NAME, DESCRIPTION,
PROTOCOL, SUBNET_ID, SUBNET, LB_METHOD, NAME, DESCRIPTION,
ADMIN_STATE_UP, VIP, MONITORS,
) = (
'protocol', 'subnet_id', 'lb_method', 'name', 'description',
'protocol', 'subnet_id', 'subnet', 'lb_method', 'name', 'description',
'admin_state_up', 'vip', 'monitors',
)
@ -181,10 +181,17 @@ class Pool(neutron.NeutronResource):
]
),
SUBNET_ID: properties.Schema(
properties.Schema.STRING,
support_status=support.SupportStatus(
support.DEPRECATED,
_('Use property %s.') % SUBNET),
required=False
),
SUBNET: properties.Schema(
properties.Schema.STRING,
_('The subnet for the port on which the members '
'of the pool will be connected.'),
required=True
required=False
),
LB_METHOD: properties.Schema(
properties.Schema.STRING,
@ -298,7 +305,8 @@ class Pool(neutron.NeutronResource):
res = super(Pool, self).validate()
if res:
return res
self._validate_depr_property_required(
self.properties, self.SUBNET, self.SUBNET_ID)
session_p = self.properties[self.VIP].get(self.VIP_SESSION_PERSISTENCE)
if session_p is None:
# session persistence is not configured, skip validation
@ -317,6 +325,8 @@ class Pool(neutron.NeutronResource):
properties = self.prepare_properties(
self.properties,
self.physical_resource_name())
self._resolve_subnet(
self.neutron(), properties, self.SUBNET, 'subnet_id')
vip_properties = properties.pop(self.VIP)
monitors = properties.pop(self.MONITORS)
client = self.neutron()
@ -339,13 +349,11 @@ class Pool(neutron.NeutronResource):
vip_arguments['protocol'] = self.properties[self.PROTOCOL]
if vip_arguments.get(self.VIP_SUBNET) is None:
vip_arguments['subnet_id'] = self.properties[self.SUBNET_ID]
vip_arguments['subnet_id'] = properties[self.SUBNET_ID]
else:
vip_arguments[
'subnet_id'] = neutronV20.find_resourceid_by_name_or_id(
self.neutron(),
'subnet',
vip_arguments.pop(self.VIP_SUBNET))
vip_arguments['subnet_id'] = self._resolve_subnet(
self.neutron(),
vip_arguments, self.VIP_SUBNET, 'subnet_id')
vip_arguments['pool_id'] = pool['id']
vip = client.create_vip({'vip': vip_arguments})['vip']

View File

@ -19,6 +19,7 @@ from heat.engine import clients
from heat.engine import constraints
from heat.engine import properties
from heat.engine.resources.neutron import neutron
from heat.engine import support
if clients.neutronclient is not None:
from neutronclient.common.exceptions import NeutronClientException
@ -42,9 +43,9 @@ class NetworkGateway(neutron.NeutronResource):
)
_CONNECTIONS_KEYS = (
NETWORK_ID, SEGMENTATION_TYPE, SEGMENTATION_ID,
NETWORK_ID, NETWORK, SEGMENTATION_TYPE, SEGMENTATION_ID,
) = (
'network_id', 'segmentation_type', 'segmentation_id',
'network_id', 'network', 'segmentation_type', 'segmentation_id',
)
properties_schema = {
@ -86,11 +87,18 @@ class NetworkGateway(neutron.NeutronResource):
properties.Schema.MAP,
schema={
NETWORK_ID: properties.Schema(
properties.Schema.STRING,
support_status=support.SupportStatus(
support.DEPRECATED,
_('Use property %s.') % NETWORK),
required=False
),
NETWORK: properties.Schema(
properties.Schema.STRING,
description=_(
'The id of internal network to connect on '
'The internal network to connect on '
'the network gateway.'),
required=True
required=False
),
SEGMENTATION_TYPE: properties.Schema(
properties.Schema.STRING,
@ -133,6 +141,8 @@ class NetworkGateway(neutron.NeutronResource):
connections = self.properties[self.CONNECTIONS]
for connection in connections:
self._validate_depr_property_required(
connection, self.NETWORK, self.NETWORK_ID)
segmentation_type = connection[self.SEGMENTATION_TYPE]
segmentation_id = connection.get(self.SEGMENTATION_ID)
@ -156,6 +166,10 @@ class NetworkGateway(neutron.NeutronResource):
{'network_gateway': props})['network_gateway']
for connection in connections:
self._resolve_network(
self.neutron(), connection, self.NETWORK, 'network_id')
if self.NETWORK in connection.keys():
connection.pop(self.NETWORK)
self.neutron().connect_network_gateway(
ret['id'], connection
)
@ -170,6 +184,10 @@ class NetworkGateway(neutron.NeutronResource):
connections = self.properties[self.CONNECTIONS]
for connection in connections:
try:
self._resolve_network(
self.neutron(), connection, self.NETWORK, 'network_id')
if self.NETWORK in connection.keys():
connection.pop(self.NETWORK)
client.disconnect_network_gateway(
self.resource_id, connection
)
@ -202,12 +220,20 @@ class NetworkGateway(neutron.NeutronResource):
if self.CONNECTIONS in prop_diff:
for connection in self.properties[self.CONNECTIONS]:
try:
self._resolve_network(
self.neutron(), connection, self.NETWORK, 'network_id')
if self.NETWORK in connection.keys():
connection.pop(self.NETWORK)
self.neutron().disconnect_network_gateway(
self.resource_id, connection
)
except NeutronClientException as ex:
self._handle_not_found_exception(ex)
for connection in connections:
self._resolve_network(
self.neutron(), connection, self.NETWORK, 'network_id')
if self.NETWORK in connection.keys():
connection.pop(self.NETWORK)
self.neutron().connect_network_gateway(
self.resource_id, connection
)

View File

@ -12,6 +12,7 @@
# under the License.
from neutronclient.common.exceptions import NeutronClientException
from neutronclient.neutron import v2_0 as neutronV20
from heat.common import exception
from heat.engine import function
@ -51,6 +52,42 @@ class NeutronResource(resource.Resource):
for k in banned_keys.intersection(vs.keys()):
return '%s not allowed in value_specs' % k
@staticmethod
def _validate_depr_property_required(properties, prop_key, depr_prop_key):
prop_value = properties.get(prop_key)
depr_prop_value = properties.get(depr_prop_key)
if prop_value and depr_prop_value:
raise exception.ResourcePropertyConflict(prop_key,
depr_prop_key)
if not prop_value and not depr_prop_value:
msg = _('Either %(prop_key)s or %(depr_prop_key)s'
' should be specified.'
) % {'prop_key': prop_key,
'depr_prop_key': depr_prop_key}
raise exception.StackValidationFailed(message=msg)
@staticmethod
def _find_neutron_resource(neutron_client, props, key, key_type):
return neutronV20.find_resourceid_by_name_or_id(
neutron_client, key_type, props.get(key))
@staticmethod
def _resolve_network(neutron_client, props, net_key, net_id_key):
if props.get(net_key):
props[net_id_key] = NeutronResource._find_neutron_resource(
neutron_client, props, net_key, 'network')
props.pop(net_key)
return props[net_id_key]
@staticmethod
def _resolve_subnet(neutron_client, props, subnet_key, subnet_id_key):
if props.get(subnet_key):
props[subnet_id_key] = NeutronResource._find_neutron_resource(
neutron_client, props, subnet_key, 'subnet')
props.pop(subnet_key)
return props[subnet_id_key]
@staticmethod
def prepare_properties(properties, name):
'''

View File

@ -15,6 +15,7 @@ from heat.engine import clients
from heat.engine import properties
from heat.engine.resources.neutron import neutron
from heat.engine.resources.neutron import subnet
from heat.engine import support
from heat.openstack.common import log as logging
if clients.neutronclient is not None:
@ -26,19 +27,21 @@ logger = logging.getLogger(__name__)
class Port(neutron.NeutronResource):
PROPERTIES = (
NETWORK_ID, NAME, VALUE_SPECS, ADMIN_STATE_UP, FIXED_IPS,
MAC_ADDRESS, DEVICE_ID, SECURITY_GROUPS, ALLOWED_ADDRESS_PAIRS,
NETWORK_ID, NETWORK, NAME, VALUE_SPECS,
ADMIN_STATE_UP, FIXED_IPS, MAC_ADDRESS,
DEVICE_ID, SECURITY_GROUPS, ALLOWED_ADDRESS_PAIRS,
DEVICE_OWNER,
) = (
'network_id', 'name', 'value_specs', 'admin_state_up', 'fixed_ips',
'mac_address', 'device_id', 'security_groups', 'allowed_address_pairs',
'network_id', 'network', 'name', 'value_specs',
'admin_state_up', 'fixed_ips', 'mac_address',
'device_id', 'security_groups', 'allowed_address_pairs',
'device_owner',
)
_FIXED_IP_KEYS = (
FIXED_IP_SUBNET_ID, FIXED_IP_IP_ADDRESS,
FIXED_IP_SUBNET_ID, FIXED_IP_SUBNET, FIXED_IP_IP_ADDRESS,
) = (
'subnet_id', 'ip_address',
'subnet_id', 'subnet', 'ip_address',
)
_ALLOWED_ADDRESS_PAIR_KEYS = (
@ -50,9 +53,16 @@ class Port(neutron.NeutronResource):
properties_schema = {
NETWORK_ID: properties.Schema(
properties.Schema.STRING,
_('Network ID this port belongs to.'),
required=True
support_status=support.SupportStatus(
support.DEPRECATED,
_('Use property %s.') % NETWORK)
),
NETWORK: properties.Schema(
properties.Schema.STRING,
_('Network this port belongs to.')
),
NAME: properties.Schema(
properties.Schema.STRING,
_('A symbolic name for this port.'),
@ -78,6 +88,12 @@ class Port(neutron.NeutronResource):
properties.Schema.MAP,
schema={
FIXED_IP_SUBNET_ID: properties.Schema(
properties.Schema.STRING,
support_status=support.SupportStatus(
support.DEPRECATED,
_('Use property %s.') % FIXED_IP_SUBNET)
),
FIXED_IP_SUBNET: properties.Schema(
properties.Schema.STRING,
_('Subnet in which to allocate the IP address for '
'this port.')
@ -151,6 +167,11 @@ class Port(neutron.NeutronResource):
update_allowed_keys = ('Properties',)
def validate(self):
super(Port, self).validate()
self._validate_depr_property_required(self.properties,
self.NETWORK, self.NETWORK_ID)
def add_dependencies(self, deps):
super(Port, self).add_dependencies(deps)
# Depend on any Subnet in this template with the same
@ -159,16 +180,21 @@ class Port(neutron.NeutronResource):
# to so all subnets in a network should be created before
# the ports in that network.
for resource in self.stack.itervalues():
if (resource.has_interface('OS::Neutron::Subnet') and
resource.properties.get(subnet.Subnet.NETWORK_ID) ==
self.properties.get(self.NETWORK_ID)):
deps += (self, resource)
if resource.has_interface('OS::Neutron::Subnet'):
dep_network = resource.properties.get(
subnet.Subnet.NETWORK) or resource.properties.get(
subnet.Subnet.NETWORK_ID)
network = self.properties.get(
self.NETWORK) or self.properties.get(self.NETWORK_ID)
if dep_network == network:
deps += (self, resource)
def handle_create(self):
props = self.prepare_properties(
self.properties,
self.physical_resource_name())
self._resolve_network(self.neutron(),
props, self.NETWORK, 'network_id')
self._prepare_list_properties(props)
if not props['fixed_ips']:
@ -182,7 +208,10 @@ class Port(neutron.NeutronResource):
for key, value in fixed_ip.items():
if value is None:
fixed_ip.pop(key)
if fixed_ip.get(self.FIXED_IP_SUBNET):
self._resolve_subnet(
self.neutron(), fixed_ip,
self.FIXED_IP_SUBNET, 'subnet_id')
# delete empty MAC addresses so that Neutron validation code
# wouldn't fail as it not accepts Nones
for pair in props.get(self.ALLOWED_ADDRESS_PAIRS, []):

View File

@ -20,7 +20,6 @@ from heat.engine import support
if clients.neutronclient is not None:
from neutronclient.common.exceptions import NeutronClientException
from neutronclient.neutron import v2_0 as neutronV20
class Router(neutron.NeutronResource):
@ -104,7 +103,9 @@ class Router(neutron.NeutronResource):
external_gw_net = external_gw.get(self.EXTERNAL_GATEWAY_NETWORK)
for res in self.stack.itervalues():
if res.has_interface('OS::Neutron::Subnet'):
subnet_net = res.properties.get(subnet.Subnet.NETWORK_ID)
subnet_net = res.properties.get(
subnet.Subnet.NETWORK) or res.properties.get(
subnet.Subnet.NETWORK_ID)
if subnet_net == external_gw_net:
deps += (self, res)
@ -112,10 +113,9 @@ class Router(neutron.NeutronResource):
props = super(Router, self).prepare_properties(properties, name)
gateway = props.get(self.EXTERNAL_GATEWAY)
if gateway:
gateway['network_id'] = neutronV20.find_resourceid_by_name_or_id(
self.neutron(),
'network',
gateway.pop(self.EXTERNAL_GATEWAY_NETWORK))
self._resolve_network(
self.neutron(), gateway,
self.EXTERNAL_GATEWAY_NETWORK, 'network_id')
if gateway[self.EXTERNAL_GATEWAY_ENABLE_SNAT] is None:
del gateway[self.EXTERNAL_GATEWAY_ENABLE_SNAT]
return props
@ -176,9 +176,9 @@ class Router(neutron.NeutronResource):
class RouterInterface(neutron.NeutronResource):
PROPERTIES = (
ROUTER_ID, SUBNET_ID, PORT_ID,
ROUTER_ID, SUBNET_ID, SUBNET, PORT_ID,
) = (
'router_id', 'subnet_id', 'port_id',
'router_id', 'subnet_id', 'subnet', 'port_id',
)
properties_schema = {
@ -189,33 +189,54 @@ class RouterInterface(neutron.NeutronResource):
),
SUBNET_ID: properties.Schema(
properties.Schema.STRING,
_('The subnet id, either subnet_id or port_id should be '
support_status=support.SupportStatus(
support.DEPRECATED,
_('Use property %s.') % SUBNET)
),
SUBNET: properties.Schema(
properties.Schema.STRING,
_('The subnet, either subnet or port_id should be '
'specified.')
),
PORT_ID: properties.Schema(
properties.Schema.STRING,
_('The port id, either subnet_id or port_id should be specified.')
_('The port id, either subnet or port_id should be specified.')
),
}
@staticmethod
def _validate_depr_subnet_keys(properties, subnet_key, depr_subnet_key):
subnet_value = properties.get(subnet_key)
subnet_id_value = properties.get(depr_subnet_key)
if subnet_value and subnet_id_value:
raise exception.ResourcePropertyConflict(subnet_key,
subnet_key)
if not subnet_value and not subnet_id_value:
return False
return True
def validate(self):
'''
Validate any of the provided params
'''
super(RouterInterface, self).validate()
subnet_id = self.properties.get(self.SUBNET_ID)
prop_subnet_exists = self._validate_depr_subnet_keys(
self.properties, self.SUBNET, self.SUBNET_ID)
port_id = self.properties.get(self.PORT_ID)
if subnet_id and port_id:
raise exception.ResourcePropertyConflict(self.SUBNET_ID,
if prop_subnet_exists and port_id:
raise exception.ResourcePropertyConflict(self.SUBNET,
self.PORT_ID)
if not subnet_id and not port_id:
msg = 'Either subnet_id or port_id must be specified.'
if not prop_subnet_exists and not port_id:
msg = 'Either subnet or port_id must be specified.'
raise exception.StackValidationFailed(message=msg)
def handle_create(self):
router_id = self.properties.get(self.ROUTER_ID)
key = self.SUBNET_ID
value = self.properties.get(key)
key = 'subnet_id'
value = self._resolve_subnet(
self.neutron(), dict(self.properties), self.SUBNET, key)
if not value:
key = self.PORT_ID
value = self.properties.get(key)
@ -250,9 +271,9 @@ class RouterGateway(neutron.NeutronResource):
)
PROPERTIES = (
ROUTER_ID, NETWORK_ID,
ROUTER_ID, NETWORK_ID, NETWORK,
) = (
'router_id', 'network_id',
'router_id', 'network_id', 'network'
)
properties_schema = {
@ -263,34 +284,52 @@ class RouterGateway(neutron.NeutronResource):
),
NETWORK_ID: properties.Schema(
properties.Schema.STRING,
_('ID of the external network for the gateway.'),
required=True
support_status=support.SupportStatus(
support.DEPRECATED,
_('Use property %s.') % NETWORK),
required=False
),
NETWORK: properties.Schema(
properties.Schema.STRING,
_('external network for the gateway.'),
required=False
),
}
def validate(self):
super(RouterGateway, self).validate()
self._validate_depr_property_required(
self.properties, self.NETWORK, self.NETWORK_ID)
def add_dependencies(self, deps):
super(RouterGateway, self).add_dependencies(deps)
for resource in self.stack.itervalues():
# depend on any RouterInterface in this template with the same
# router_id as this router_id
if (resource.has_interface('OS::Neutron::RouterInterface') and
resource.properties.get(RouterInterface.ROUTER_ID) ==
self.properties.get(self.ROUTER_ID)):
deps += (self, resource)
if resource.has_interface('OS::Neutron::RouterInterface'):
dep_router_id = resource.properties.get(
RouterInterface.ROUTER_ID)
router_id = self.properties.get(self.ROUTER_ID)
if dep_router_id == router_id:
deps += (self, resource)
# depend on any subnet in this template with the same network_id
# as this network_id, as the gateway implicitly creates a port
# on that subnet
elif (resource.has_interface('OS::Neutron::Subnet') and
resource.properties.get(subnet.Subnet.NETWORK_ID) ==
self.properties.get(self.NETWORK_ID)):
deps += (self, resource)
if resource.has_interface('OS::Neutron::Subnet'):
dep_network = resource.properties.get(
subnet.Subnet.NETWORK) or resource.properties.get(
subnet.Subnet.NETWORK_ID)
network = self.properties.get(
self.NETWORK) or self.properties.get(self.NETWORK_ID)
if dep_network == network:
deps += (self, resource)
def handle_create(self):
router_id = self.properties.get(self.ROUTER_ID)
network_id = neutronV20.find_resourceid_by_name_or_id(
self.neutron(),
'network',
self.properties.get(self.NETWORK_ID))
network_id = self._resolve_network(
self.neutron(), dict(self.properties), self.NETWORK,
'network_id')
self.neutron().add_gateway_router(
router_id,
{'network_id': network_id})

View File

@ -15,6 +15,7 @@ from heat.engine import clients
from heat.engine import constraints
from heat.engine import properties
from heat.engine.resources.neutron import neutron
from heat.engine import support
if clients.neutronclient is not None:
from neutronclient.common.exceptions import NeutronClientException
@ -23,11 +24,11 @@ if clients.neutronclient is not None:
class Subnet(neutron.NeutronResource):
PROPERTIES = (
NETWORK_ID, CIDR, VALUE_SPECS, NAME, IP_VERSION,
NETWORK_ID, NETWORK, CIDR, VALUE_SPECS, NAME, IP_VERSION,
DNS_NAMESERVERS, GATEWAY_IP, ENABLE_DHCP, ALLOCATION_POOLS,
TENANT_ID, HOST_ROUTES,
) = (
'network_id', 'cidr', 'value_specs', 'name', 'ip_version',
'network_id', 'network', 'cidr', 'value_specs', 'name', 'ip_version',
'dns_nameservers', 'gateway_ip', 'enable_dhcp', 'allocation_pools',
'tenant_id', 'host_routes',
)
@ -46,9 +47,16 @@ class Subnet(neutron.NeutronResource):
properties_schema = {
NETWORK_ID: properties.Schema(
properties.Schema.STRING,
support_status=support.SupportStatus(
support.DEPRECATED,
_('Use property %s.') % NETWORK),
required=False
),
NETWORK: properties.Schema(
properties.Schema.STRING,
_('The ID of the attached network.'),
required=True
required=False
),
CIDR: properties.Schema(
properties.Schema.STRING,
@ -160,11 +168,17 @@ class Subnet(neutron.NeutronResource):
if props.get(cls.GATEWAY_IP) == '':
props[cls.GATEWAY_IP] = None
def validate(self):
super(Subnet, self).validate()
self._validate_depr_property_required(self.properties,
self.NETWORK, self.NETWORK_ID)
def handle_create(self):
props = self.prepare_properties(
self.properties,
self.physical_resource_name())
self._resolve_network(
self.neutron(), props, self.NETWORK, 'network_id')
self._null_gateway_ip(props)
subnet = self.neutron().create_subnet({'subnet': props})['subnet']

View File

@ -15,6 +15,7 @@ from heat.engine import clients
from heat.engine import constraints
from heat.engine import properties
from heat.engine.resources.neutron import neutron
from heat.engine import support
if clients.neutronclient is not None:
from neutronclient.common.exceptions import NeutronClientException
@ -26,9 +27,11 @@ class VPNService(neutron.NeutronResource):
"""
PROPERTIES = (
NAME, DESCRIPTION, ADMIN_STATE_UP, SUBNET_ID, ROUTER_ID,
NAME, DESCRIPTION, ADMIN_STATE_UP,
SUBNET_ID, SUBNET, ROUTER_ID,
) = (
'name', 'description', 'admin_state_up', 'subnet_id', 'router_id',
'name', 'description', 'admin_state_up',
'subnet_id', 'subnet', 'router_id',
)
properties_schema = {
@ -50,9 +53,15 @@ class VPNService(neutron.NeutronResource):
),
SUBNET_ID: properties.Schema(
properties.Schema.STRING,
_('Unique identifier for the subnet in which the vpn service '
'will be created.'),
required=True
support_status=support.SupportStatus(
support.DEPRECATED,
_('Use property %s.') % SUBNET),
required=False
),
SUBNET: properties.Schema(
properties.Schema.STRING,
_('Subnet in which the vpn service will be created.'),
required=False
),
ROUTER_ID: properties.Schema(
properties.Schema.STRING,
@ -81,10 +90,18 @@ class VPNService(neutron.NeutronResource):
def _show_resource(self):
return self.neutron().show_vpnservice(self.resource_id)['vpnservice']
def validate(self):
super(VPNService, self).validate()
self._validate_depr_property_required(
self.properties, self.SUBNET, self.SUBNET_ID)
def handle_create(self):
props = self.prepare_properties(
self.properties,
self.physical_resource_name())
self._resolve_subnet(
self.neutron(), props,
self.SUBNET, 'subnet_id')
vpnservice = self.neutron().create_vpnservice({'vpnservice': props})[
'vpnservice']
self.resource_id_set(vpnservice['id'])

View File

@ -22,6 +22,7 @@ from heat.engine import clients
from heat.engine import properties
from heat.engine import resource
from heat.engine.resources.neutron import net
from heat.engine.resources.neutron import neutron
from heat.engine.resources.neutron.neutron import NeutronResource as qr
from heat.engine.resources.neutron import provider_net
from heat.engine.resources.neutron import router
@ -61,6 +62,85 @@ neutron_template = '''
"admin_state_up": false
}
},
"subnet": {
"Type": "OS::Neutron::Subnet",
"Properties": {
"network": { "Ref" : "network" },
"tenant_id": "c1210485b2424d48804aad5d39c61b8f",
"ip_version": 4,
"cidr": "10.0.3.0/24",
"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"}],
"dns_nameservers": ["8.8.8.8"]
}
},
"port": {
"Type": "OS::Neutron::Port",
"Properties": {
"device_id": "d6b4d3a5-c700-476f-b609-1493dd9dadc0",
"name": "port1",
"network": { "Ref" : "network" },
"fixed_ips": [{
"subnet": { "Ref" : "subnet" },
"ip_address": "10.0.3.21"
}]
}
},
"port2": {
"Type": "OS::Neutron::Port",
"Properties": {
"name": "port2",
"network": { "Ref" : "network" }
}
},
"router": {
"Type": "OS::Neutron::Router",
"Properties": {
"l3_agent_id": "792ff887-6c85-4a56-b518-23f24fa65581"
}
},
"router_interface": {
"Type": "OS::Neutron::RouterInterface",
"Properties": {
"router_id": { "Ref" : "router" },
"subnet": { "Ref" : "subnet" }
}
},
"gateway": {
"Type": "OS::Neutron::RouterGateway",
"Properties": {
"router_id": { "Ref" : "router" },
"network": { "Ref" : "network" }
}
}
}
}
'''
neutron_template_deprecated = '''
{
"AWSTemplateFormatVersion" : "2010-09-09",
"Description" : "Template to test Neutron resources",
"Parameters" : {},
"Resources" : {
"network": {
"Type": "OS::Neutron::Net",
"Properties": {
"name": "the_network",
"tenant_id": "c1210485b2424d48804aad5d39c61b8f",
"shared": true
}
},
"unnamed_network": {
"Type": "OS::Neutron::Net"
},
"admin_down_network": {
"Type": "OS::Neutron::Net",
"Properties": {
"admin_state_up": false
}
},
"subnet": {
"Type": "OS::Neutron::Subnet",
"Properties": {
@ -156,7 +236,7 @@ neutron_external_gateway_template = '''
}
'''
neutron_floating_template = '''
neutron_floating_template_deprecated = '''
{
"AWSTemplateFormatVersion" : "2010-09-09",
"Description" : "Template to test Neutron resources",
@ -199,7 +279,50 @@ neutron_floating_template = '''
}
'''
neutron_port_template = '''
neutron_floating_template = '''
{
"AWSTemplateFormatVersion" : "2010-09-09",
"Description" : "Template to test Neutron resources",
"Parameters" : {},
"Resources" : {
"port_floating": {
"Type": "OS::Neutron::Port",
"Properties": {
"network": "xyz1234",
"fixed_ips": [{
"subnet": "sub1234",
"ip_address": "10.0.0.10"
}]
}
},
"floating_ip": {
"Type": "OS::Neutron::FloatingIP",
"Properties": {
"floating_network": "abcd1234",
}
},
"floating_ip_assoc": {
"Type": "OS::Neutron::FloatingIPAssociation",
"Properties": {
"floatingip_id": { "Ref" : "floating_ip" },
"port_id": { "Ref" : "port_floating" }
}
},
"router": {
"Type": "OS::Neutron::Router"
},
"gateway": {
"Type": "OS::Neutron::RouterGateway",
"Properties": {
"router_id": { "Ref" : "router" },
"network": "abcd1234"
}
}
}
}
'''
neutron_port_template_deprecated = '''
{
"AWSTemplateFormatVersion" : "2010-09-09",
"Description" : "Template to test Neutron resources",
@ -220,6 +343,28 @@ neutron_port_template = '''
}
'''
neutron_port_template = '''
{
"AWSTemplateFormatVersion" : "2010-09-09",
"Description" : "Template to test Neutron resources",
"Parameters" : {},
"Resources" : {
"port": {
"Type": "OS::Neutron::Port",
"Properties": {
"network": "net1234",
"fixed_ips": [{
"subnet": "sub1234",
"ip_address": "10.0.3.21"
}],
"device_owner": "network:dhcp"
}
}
}
}
'''
neutron_port_with_address_pair_template = '''
{
"AWSTemplateFormatVersion" : "2010-09-09",
@ -229,7 +374,7 @@ neutron_port_with_address_pair_template = '''
"port": {
"Type": "OS::Neutron::Port",
"Properties": {
"network_id": "abcd1234",
"network": "abcd1234",
"allowed_address_pairs": [{
"ip_address": "10.0.3.21",
"mac_address": "00-B0-D0-86-BB-F7"
@ -330,6 +475,19 @@ class NeutronTest(HeatTestCase):
vs['foo'] = '1234'
self.assertIsNone(qr.validate_properties(p))
def test_validate_depr_properties_required(self):
data = {'network_id': '1234',
'network': 'abc'}
p = properties.Properties(subnet.Subnet.properties_schema, data)
self.assertRaises(exception.ResourcePropertyConflict,
qr._validate_depr_property_required,
p, 'network', 'network_id')
data = {}
p = properties.Properties(subnet.Subnet.properties_schema, data)
self.assertRaises(exception.StackValidationFailed,
qr._validate_depr_property_required,
p, 'network', 'network_id')
def test_prepare_properties(self):
data = {'admin_state_up': False,
'value_specs': {'router:external': True}}
@ -570,6 +728,7 @@ class NeutronNetTest(HeatTestCase):
@skipIf(neutronclient is None, 'neutronclient unavailable')
class NeutronProviderNetTest(HeatTestCase):
def setUp(self):
super(NeutronProviderNetTest, self).setUp()
self.m.StubOutWithMock(neutronclient.Client, 'create_network')
@ -698,17 +857,86 @@ class NeutronSubnetTest(HeatTestCase):
self.m.StubOutWithMock(neutronclient.Client, 'delete_subnet')
self.m.StubOutWithMock(neutronclient.Client, 'show_subnet')
self.m.StubOutWithMock(neutronclient.Client, 'update_subnet')
self.m.StubOutWithMock(neutron.neutronV20,
'find_resourceid_by_name_or_id')
self.m.StubOutWithMock(clients.OpenStackClients, 'keystone')
def create_subnet(self, t, stack, resource_name):
rsrc = subnet.Subnet('test_subnet', t['Resources'][resource_name],
stack)
scheduler.TaskRunner(rsrc.create)()
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
return rsrc
def test_subnet(self):
t = self._test_subnet()
neutron.neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'network',
'None'
).AndReturn('None')
stack = utils.parse_stack(t)
rsrc = self.create_subnet(t, stack, 'subnet')
self.m.ReplayAll()
scheduler.TaskRunner(rsrc.create)()
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
rsrc.validate()
ref_id = rsrc.FnGetRefId()
self.assertEqual('91e47a57-7508-46fe-afc9-fc454e8580e1', ref_id)
self.assertIsNone(rsrc.FnGetAtt('network_id'))
self.assertEqual('fc68ea2c-b60b-4b4f-bd82-94ec81110766',
rsrc.FnGetAtt('network_id'))
self.assertEqual('8.8.8.8', rsrc.FnGetAtt('dns_nameservers')[0])
# assert the dependency (implicit or explicit) between the ports
# and the subnet
self.assertIn(stack['port'], stack.dependencies[stack['subnet']])
self.assertIn(stack['port2'], stack.dependencies[stack['subnet']])
update_snippet = {
"Type": "OS::Neutron::Subnet",
"Properties": {
"name": 'mysubnet',
"network": {"Ref": "network"},
"tenant_id": "c1210485b2424d48804aad5d39c61b8f",
"ip_version": 4,
"cidr": "10.0.3.0/24",
"allocation_pools": [
{"start": "10.0.3.20", "end": "10.0.3.150"}],
"dns_nameservers": ["8.8.8.8", "192.168.1.254"]
}
}
rsrc.handle_update(stack.resolve_static_data(update_snippet), {}, {})
self.assertIsNone(scheduler.TaskRunner(rsrc.delete)())
rsrc.state_set(rsrc.CREATE, rsrc.COMPLETE, 'to delete again')
self.assertIsNone(scheduler.TaskRunner(rsrc.delete)())
self.m.VerifyAll()
def test_subnet_deprecated(self):
t = self._test_subnet(resolve_neutron=False)
stack = utils.parse_stack(t)
rsrc = self.create_subnet(t, stack, 'subnet')
self.m.ReplayAll()
scheduler.TaskRunner(rsrc.create)()
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
rsrc.validate()
ref_id = rsrc.FnGetRefId()
self.assertEqual('91e47a57-7508-46fe-afc9-fc454e8580e1', ref_id)
self.assertIsNone(rsrc.FnGetAtt('network_id'))
self.assertEqual('fc68ea2c-b60b-4b4f-bd82-94ec81110766',
rsrc.FnGetAtt('network_id'))
self.assertEqual('8.8.8.8', rsrc.FnGetAtt('dns_nameservers')[0])
# assert the dependency (implicit or explicit) between the ports
# and the subnet
self.assertIn(stack['port'], stack.dependencies[stack['subnet']])
self.assertIn(stack['port2'], stack.dependencies[stack['subnet']])
self.assertIsNone(scheduler.TaskRunner(rsrc.delete)())
rsrc.state_set(rsrc.CREATE, rsrc.COMPLETE, 'to delete again')
self.assertIsNone(scheduler.TaskRunner(rsrc.delete)())
self.m.VerifyAll()
def _test_subnet(self, resolve_neutron=True):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
neutronclient.Client.create_subnet({
@ -769,16 +997,6 @@ class NeutronSubnetTest(HeatTestCase):
neutronclient.Client.show_subnet(
'91e47a57-7508-46fe-afc9-fc454e8580e1').AndReturn(sn)
# Update script
neutronclient.Client.update_subnet(
'91e47a57-7508-46fe-afc9-fc454e8580e1',
{'subnet': {
'dns_nameservers': ['8.8.8.8', '192.168.1.254'],
'name': 'mysubnet',
'enable_dhcp': True
}}
)
# Delete script
neutronclient.Client.delete_subnet(
'91e47a57-7508-46fe-afc9-fc454e8580e1'
@ -792,51 +1010,32 @@ class NeutronSubnetTest(HeatTestCase):
'91e47a57-7508-46fe-afc9-fc454e8580e1'
).AndRaise(qe.NeutronClientException(status_code=404))
self.m.ReplayAll()
t = template_format.parse(neutron_template)
stack = utils.parse_stack(t)
rsrc = self.create_subnet(t, stack, 'subnet')
if resolve_neutron:
t = template_format.parse(neutron_template)
# Update script
neutronclient.Client.update_subnet(
'91e47a57-7508-46fe-afc9-fc454e8580e1',
{'subnet': {
'dns_nameservers': ['8.8.8.8', '192.168.1.254'],
'name': 'mysubnet',
'enable_dhcp': True
}}
)
rsrc.validate()
else:
t = template_format.parse(neutron_template_deprecated)
ref_id = rsrc.FnGetRefId()
self.assertEqual('91e47a57-7508-46fe-afc9-fc454e8580e1', ref_id)
self.assertIsNone(rsrc.FnGetAtt('network_id'))
self.assertEqual('fc68ea2c-b60b-4b4f-bd82-94ec81110766',
rsrc.FnGetAtt('network_id'))
self.assertEqual('8.8.8.8', rsrc.FnGetAtt('dns_nameservers')[0])
# assert the dependency (implicit or explicit) between the ports
# and the subnet
self.assertIn(stack['port'], stack.dependencies[stack['subnet']])
self.assertIn(stack['port2'], stack.dependencies[stack['subnet']])
update_snippet = {
"Type": "OS::Neutron::Subnet",
"Properties": {
"name": 'mysubnet',
"network_id": {"Ref": "network"},
"tenant_id": "c1210485b2424d48804aad5d39c61b8f",
"ip_version": 4,
"cidr": "10.0.3.0/24",
"allocation_pools": [
{"start": "10.0.3.20", "end": "10.0.3.150"}],
"dns_nameservers": ["8.8.8.8", "192.168.1.254"],
'host_routes': [
{'destination': u'10.0.4.0/24', 'nexthop': u'10.0.3.20'}]
}
}
rsrc.handle_update(stack.resolve_static_data(update_snippet), {}, {})
self.assertIsNone(scheduler.TaskRunner(rsrc.delete)())
rsrc.state_set(rsrc.CREATE, rsrc.COMPLETE, 'to delete again')
self.assertIsNone(scheduler.TaskRunner(rsrc.delete)())
self.m.VerifyAll()
return t
def test_subnet_disable_dhcp(self):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
neutron.neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'network',
'None'
).AndReturn('None')
neutronclient.Client.create_subnet({
'subnet': {
'name': utils.PhysName('test_stack', 'test_subnet'),
@ -903,6 +1102,9 @@ class NeutronSubnetTest(HeatTestCase):
stack = utils.parse_stack(t)
rsrc = self.create_subnet(t, stack, 'subnet')
self.m.ReplayAll()
scheduler.TaskRunner(rsrc.create)()
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
rsrc.validate()
ref_id = rsrc.FnGetRefId()
@ -956,7 +1158,7 @@ class NeutronSubnetTest(HeatTestCase):
@skipIf(neutronclient is None, 'neutronclient unavailable')
class NeutronRouterTest(HeatTestCase):
@skipIf(router.neutronV20 is None, "Missing Neutron v2_0")
@skipIf(neutron.neutronV20 is None, "Missing Neutron v2_0")
def setUp(self):
super(NeutronRouterTest, self).setUp()
self.m.StubOutWithMock(neutronclient.Client, 'create_router')
@ -973,7 +1175,7 @@ class NeutronRouterTest(HeatTestCase):
'remove_router_from_l3_agent')
self.m.StubOutWithMock(neutronclient.Client,
'list_l3_agent_hosting_routers')
self.m.StubOutWithMock(router.neutronV20,
self.m.StubOutWithMock(neutron.neutronV20,
'find_resourceid_by_name_or_id')
self.m.StubOutWithMock(clients.OpenStackClients, 'keystone')
@ -1180,6 +1382,12 @@ class NeutronRouterTest(HeatTestCase):
self.assertIn(stack['router'], deps)
def test_router_interface(self):
self._test_router_interface()
def test_router_interface_deprecated(self):
self._test_router_interface(resolve_neutron=False)
def _test_router_interface(self, resolve_neutron=True):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
neutronclient.Client.add_interface_router(
@ -1194,16 +1402,27 @@ class NeutronRouterTest(HeatTestCase):
'3e46229d-8fce-4733-819a-b5fe630550f8',
{'subnet_id': '91e47a57-7508-46fe-afc9-fc454e8580e1'}
).AndRaise(qe.NeutronClientException(status_code=404))
self.m.ReplayAll()
t = template_format.parse(neutron_template)
stack = utils.parse_stack(t)
rsrc = self.create_router_interface(
t, stack, 'router_interface', properties={
'router_id': '3e46229d-8fce-4733-819a-b5fe630550f8',
'subnet_id': '91e47a57-7508-46fe-afc9-fc454e8580e1'
})
if resolve_neutron:
neutron.neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'subnet',
'91e47a57-7508-46fe-afc9-fc454e8580e1'
).AndReturn('91e47a57-7508-46fe-afc9-fc454e8580e1')
self.m.ReplayAll()
rsrc = self.create_router_interface(
t, stack, 'router_interface', properties={
'router_id': '3e46229d-8fce-4733-819a-b5fe630550f8',
'subnet': '91e47a57-7508-46fe-afc9-fc454e8580e1'
})
else:
self.m.ReplayAll()
rsrc = self.create_router_interface(
t, stack, 'router_interface', properties={
'router_id': '3e46229d-8fce-4733-819a-b5fe630550f8',
'subnet_id': '91e47a57-7508-46fe-afc9-fc454e8580e1'
})
scheduler.TaskRunner(rsrc.delete)()
rsrc.state_set(rsrc.CREATE, rsrc.COMPLETE, 'to delete again')
scheduler.TaskRunner(rsrc.delete)()
@ -1304,13 +1523,13 @@ class NeutronRouterTest(HeatTestCase):
stack = utils.parse_stack(t)
res = router.RouterInterface('router_interface', json, stack)
ex = self.assertRaises(exception.StackValidationFailed, res.validate)
self.assertEqual("Either subnet_id or port_id must be specified.",
self.assertEqual("Either subnet or port_id must be specified.",
str(ex))
def test_gateway_router(self):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
router.neutronV20.find_resourceid_by_name_or_id(
neutron.neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'network',
'fc68ea2c-b60b-4b4f-bd82-94ec81110766'
@ -1332,7 +1551,7 @@ class NeutronRouterTest(HeatTestCase):
rsrc = self.create_gateway_router(
t, stack, 'gateway', properties={
'router_id': '3e46229d-8fce-4733-819a-b5fe630550f8',
'network_id': 'fc68ea2c-b60b-4b4f-bd82-94ec81110766'
'network': 'fc68ea2c-b60b-4b4f-bd82-94ec81110766'
})
scheduler.TaskRunner(rsrc.delete)()
@ -1344,7 +1563,7 @@ class NeutronRouterTest(HeatTestCase):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
router.neutronV20.find_resourceid_by_name_or_id(
neutron.neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'network',
'public'
@ -1426,7 +1645,7 @@ class NeutronRouterTest(HeatTestCase):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
router.neutronV20.find_resourceid_by_name_or_id(
neutron.neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'network',
'public'
@ -1487,7 +1706,7 @@ class NeutronRouterTest(HeatTestCase):
def test_update_router_gateway_as_property(self):
self._create_router_with_gateway()
router.neutronV20.find_resourceid_by_name_or_id(
neutron.neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'network',
'other_public'
@ -1572,9 +1791,17 @@ class NeutronFloatingIPTest(HeatTestCase):
self.m.StubOutWithMock(neutronclient.Client, 'delete_port')
self.m.StubOutWithMock(neutronclient.Client, 'update_port')
self.m.StubOutWithMock(neutronclient.Client, 'show_port')
self.m.StubOutWithMock(neutron.neutronV20,
'find_resourceid_by_name_or_id')
self.m.StubOutWithMock(clients.OpenStackClients, 'keystone')
def test_floating_ip(self):
self._test_floating_ip()
def test_floating_ip_deprecated(self):
self._test_floating_ip(resolve_neutron=False)
def _test_floating_ip(self, resolve_neutron=True):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
@ -1600,14 +1827,24 @@ class NeutronFloatingIPTest(HeatTestCase):
neutronclient.Client.delete_floatingip(
'fc68ea2c-b60b-4b4f-bd82-94ec81110766').AndRaise(
qe.NeutronClientException(status_code=404))
self.m.ReplayAll()
if resolve_neutron:
t = template_format.parse(neutron_floating_template)
neutron.neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'network',
'abcd1234'
).AndReturn('abcd1234')
else:
t = template_format.parse(neutron_floating_template_deprecated)
t = template_format.parse(neutron_floating_template)
stack = utils.parse_stack(t)
# assert the implicit dependency between the floating_ip
# and the gateway
self.m.ReplayAll()
deps = stack.dependencies[stack['gateway']]
self.assertIn(stack['floating_ip'], deps)
fip = stack['floating_ip']
@ -1637,6 +1874,16 @@ class NeutronFloatingIPTest(HeatTestCase):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
neutron.neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'network',
'xyz1234'
).AndReturn('xyz1234')
neutron.neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'subnet',
'sub1234'
).AndReturn('sub1234')
neutronclient.Client.create_port({'port': {
'network_id': u'xyz1234',
'fixed_ips': [
@ -1708,7 +1955,7 @@ class NeutronFloatingIPTest(HeatTestCase):
update_snippet = {
"Type": "OS::Neutron::Port",
"Properties": {
"network_id": "xyz1234",
"network": "xyz1234",
"fixed_ips": [{
"subnet_id": "sub1234",
"ip_address": "10.0.0.11"
@ -1727,6 +1974,21 @@ class NeutronFloatingIPTest(HeatTestCase):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
neutron.neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'network',
'abcd1234'
).AndReturn('abcd1234')
neutron.neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'network',
'xyz1234'
).AndReturn('xyz1234')
neutron.neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'subnet',
'sub1234'
).AndReturn('sub1234')
neutronclient.Client.create_floatingip({
'floatingip': {'floating_network_id': u'abcd1234'}
}).AndReturn({'floatingip': {
@ -1842,11 +2104,18 @@ class NeutronPortTest(HeatTestCase):
super(NeutronPortTest, self).setUp()
self.m.StubOutWithMock(neutronclient.Client, 'create_port')
self.m.StubOutWithMock(neutronclient.Client, 'show_port')
self.m.StubOutWithMock(neutron.neutronV20,
'find_resourceid_by_name_or_id')
self.m.StubOutWithMock(clients.OpenStackClients, 'keystone')
def test_missing_subnet_id(self):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
neutron.neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'network',
'net1234'
).AndReturn('net1234')
neutronclient.Client.create_port({'port': {
'network_id': u'net1234',
'fixed_ips': [
@ -1869,7 +2138,7 @@ class NeutronPortTest(HeatTestCase):
self.m.ReplayAll()
t = template_format.parse(neutron_port_template)
t['Resources']['port']['Properties']['fixed_ips'][0].pop('subnet_id')
t['Resources']['port']['Properties']['fixed_ips'][0].pop('subnet')
stack = utils.parse_stack(t)
port = stack['port']
@ -1880,6 +2149,17 @@ class NeutronPortTest(HeatTestCase):
def test_missing_ip_address(self):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
neutron.neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'network',
'net1234'
).AndReturn('net1234')
neutron.neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'subnet',
'sub1234'
).AndReturn('sub1234')
neutronclient.Client.create_port({'port': {
'network_id': u'net1234',
'fixed_ips': [
@ -1912,6 +2192,11 @@ class NeutronPortTest(HeatTestCase):
def test_missing_fixed_ips(self):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
neutron.neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'network',
'net1234'
).AndReturn('net1234')
neutronclient.Client.create_port({'port': {
'network_id': u'net1234',
'name': utils.PhysName('test_stack', 'port'),
@ -1945,6 +2230,11 @@ class NeutronPortTest(HeatTestCase):
def test_allowed_address_pair(self):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
neutron.neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'network',
'abcd1234'
).AndReturn('abcd1234')
neutronclient.Client.create_port({'port': {
'network_id': u'abcd1234',
'allowed_address_pairs': [{
@ -1976,6 +2266,11 @@ class NeutronPortTest(HeatTestCase):
def test_missing_mac_address(self):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
neutron.neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'network',
'abcd1234'
).AndReturn('abcd1234')
neutronclient.Client.create_port({'port': {
'network_id': u'abcd1234',
'allowed_address_pairs': [{
@ -2009,6 +2304,16 @@ class NeutronPortTest(HeatTestCase):
def test_security_groups(self):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
neutron.neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'network',
'net1234'
).AndReturn('net1234')
neutron.neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'subnet',
'sub1234'
).AndReturn('sub1234')
neutronclient.Client.create_port({'port': {
'network_id': u'net1234',
'security_groups': ['8a2f582a-e1cd-480f-b85d-b02631c10656',

View File

@ -20,6 +20,7 @@ from heat.common import exception
from heat.common import template_format
from heat.engine import clients
from heat.engine.resources.neutron import loadbalancer
from heat.engine.resources.neutron import neutron
from heat.engine import scheduler
from heat.openstack.common.importutils import try_import
from heat.tests.common import HeatTestCase
@ -58,7 +59,7 @@ pool_template_with_vip_subnet = '''
"Type": "OS::Neutron::Pool",
"Properties": {
"protocol": "HTTP",
"subnet_id": "sub123",
"subnet": "sub123",
"lb_method": "ROUND_ROBIN",
"vip": {
"protocol_port": 80,
@ -69,7 +70,8 @@ pool_template_with_vip_subnet = '''
}
}
'''
pool_template = '''
pool_template_deprecated = '''
{
"AWSTemplateFormatVersion" : "2010-09-09",
"Description" : "Template to test load balancer resources",
@ -90,6 +92,28 @@ pool_template = '''
}
'''
pool_template = '''
{
"AWSTemplateFormatVersion" : "2010-09-09",
"Description" : "Template to test load balancer resources",
"Parameters" : {},
"Resources" : {
"pool": {
"Type": "OS::Neutron::Pool",
"Properties": {
"protocol": "HTTP",
"subnet": "sub123",
"lb_method": "ROUND_ROBIN",
"vip": {
"protocol_port": 80
}
}
}
}
}
'''
member_template = '''
{
"AWSTemplateFormatVersion" : "2010-09-09",
@ -136,7 +160,7 @@ pool_with_session_persistence_template = '''
"Type": "OS::Neutron::Pool",
"Properties": {
"protocol": "HTTP",
"subnet_id": "sub123",
"subnet": "sub123",
"lb_method": "ROUND_ROBIN",
"vip": {
"protocol_port": 80,
@ -338,13 +362,13 @@ class PoolTest(HeatTestCase):
self.m.StubOutWithMock(neutronclient.Client,
'disassociate_health_monitor')
self.m.StubOutWithMock(neutronclient.Client, 'create_vip')
self.m.StubOutWithMock(loadbalancer.neutronV20,
self.m.StubOutWithMock(neutron.neutronV20,
'find_resourceid_by_name_or_id')
self.m.StubOutWithMock(neutronclient.Client, 'delete_vip')
self.m.StubOutWithMock(neutronclient.Client, 'show_vip')
self.m.StubOutWithMock(clients.OpenStackClients, 'keystone')
def create_pool(self, with_vip_subnet=False):
def create_pool(self, resolve_neutron=True, with_vip_subnet=False):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
neutronclient.Client.create_pool({
@ -353,7 +377,10 @@ class PoolTest(HeatTestCase):
'name': utils.PhysName('test_stack', 'pool'),
'lb_method': 'ROUND_ROBIN', 'admin_state_up': True}}
).AndReturn({'pool': {'id': '5678'}})
neutronclient.Client.show_pool('5678').AndReturn(
{'pool': {'status': 'ACTIVE'}})
neutronclient.Client.show_vip('xyz').AndReturn(
{'vip': {'status': 'ACTIVE'}})
stvipvsn = {
'vip': {
'protocol': u'HTTP', 'name': 'pool.vip',
@ -364,38 +391,52 @@ class PoolTest(HeatTestCase):
stvippsn = copy.deepcopy(stvipvsn)
stvippsn['vip']['subnet_id'] = 'sub123'
if with_vip_subnet:
if resolve_neutron and with_vip_subnet:
neutron.neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'subnet',
'sub123'
).AndReturn('sub123')
neutron.neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'subnet',
'sub9999'
).AndReturn('sub9999')
snippet = template_format.parse(pool_template_with_vip_subnet)
neutronclient.Client.create_vip(stvipvsn
).AndReturn({'vip': {'id': 'xyz'}})
snippet = template_format.parse(pool_template_with_vip_subnet)
else:
elif resolve_neutron and not with_vip_subnet:
neutron.neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'subnet',
'sub123'
).AndReturn('sub123')
snippet = template_format.parse(pool_template)
neutronclient.Client.create_vip(stvippsn
).AndReturn({'vip': {'id': 'xyz'}})
else:
snippet = template_format.parse(pool_template_deprecated)
neutronclient.Client.create_vip(stvippsn
).AndReturn({'vip': {'id': 'xyz'}})
snippet = template_format.parse(pool_template)
neutronclient.Client.show_pool('5678').AndReturn(
{'pool': {'status': 'ACTIVE'}})
neutronclient.Client.show_vip('xyz').AndReturn(
{'vip': {'status': 'ACTIVE'}})
stack = utils.parse_stack(snippet)
return loadbalancer.Pool(
'pool', snippet['Resources']['pool'], stack)
def test_create(self):
rsrc = self.create_pool()
self._test_create()
def test_create_deprecated(self):
self._test_create(resolve_neutron=False, with_vip_subnet=False)
def _test_create(self, resolve_neutron=True, with_vip_subnet=False):
rsrc = self.create_pool(resolve_neutron, with_vip_subnet)
self.m.ReplayAll()
scheduler.TaskRunner(rsrc.create)()
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
self.m.VerifyAll()
def test_create_with_vip_subnet(self):
loadbalancer.neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'subnet',
'sub9999'
).AndReturn('sub9999')
rsrc = self.create_pool(with_vip_subnet=True)
self.m.ReplayAll()
scheduler.TaskRunner(rsrc.create)()
@ -405,6 +446,12 @@ class PoolTest(HeatTestCase):
def test_create_pending(self):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
neutron.neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'subnet',
'sub123'
).AndReturn('sub123')
neutronclient.Client.create_pool({
'pool': {
'subnet_id': 'sub123', 'protocol': u'HTTP',
@ -438,6 +485,12 @@ class PoolTest(HeatTestCase):
def test_create_failed_unexpected_status(self):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
neutron.neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'subnet',
'sub123'
).AndReturn('sub123')
neutronclient.Client.create_pool({
'pool': {
'subnet_id': 'sub123', 'protocol': u'HTTP',
@ -470,6 +523,12 @@ class PoolTest(HeatTestCase):
def test_create_failed_unexpected_vip_status(self):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
neutron.neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'subnet',
'sub123'
).AndReturn('sub123')
neutronclient.Client.create_pool({
'pool': {
'subnet_id': 'sub123', 'protocol': u'HTTP',
@ -504,6 +563,12 @@ class PoolTest(HeatTestCase):
def test_create_failed(self):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
neutron.neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'subnet',
'sub123'
).AndReturn('sub123')
neutronclient.Client.create_pool({
'pool': {
'subnet_id': 'sub123', 'protocol': u'HTTP',
@ -527,6 +592,11 @@ class PoolTest(HeatTestCase):
def test_create_with_session_persistence(self):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
neutron.neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'subnet',
'sub123'
).AndReturn('sub123')
neutronclient.Client.create_pool({
'pool': {
'subnet_id': 'sub123', 'protocol': u'HTTP',
@ -584,6 +654,12 @@ class PoolTest(HeatTestCase):
def test_properties_are_prepared_for_session_persistence(self):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
neutron.neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'subnet',
'sub123'
).AndReturn('sub123')
neutronclient.Client.create_pool({
'pool': {
'subnet_id': 'sub123', 'protocol': u'HTTP',
@ -728,6 +804,11 @@ class PoolTest(HeatTestCase):
def test_update_monitors(self):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
neutron.neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'subnet',
'sub123'
).AndReturn('sub123')
neutronclient.Client.create_pool({
'pool': {
'subnet_id': 'sub123', 'protocol': u'HTTP',

View File

@ -15,12 +15,14 @@
# limitations under the License.
from mox import IgnoreArg
import mox
from testtools import skipIf
from heat.common import exception
from heat.common import template_format
from heat.engine import clients
from heat.engine.resources.neutron import network_gateway
from heat.engine.resources.neutron import neutron
from heat.engine import scheduler
from heat.openstack.common.importutils import try_import
from heat.tests.common import HeatTestCase
@ -32,6 +34,29 @@ neutronV20 = try_import('neutronclient.neutron.v2_0')
qe = try_import('neutronclient.common.exceptions')
gw_template_deprecated = '''
{
'AWSTemplateFormatVersion': '2010-09-09',
'Description': 'Template to test Network Gateway resource',
'Parameters': {},
'Resources': {
'NetworkGateway': {
'Type': 'OS::Neutron::NetworkGateway',
'Properties': {
'name': 'NetworkGateway',
'devices': [{
'id': 'e52148ca-7db9-4ec3-abe6-2c7c0ff316eb',
'interface_name': 'breth1'}],
'connections': [{
'network_id': '6af055d3-26f6-48dd-a597-7611d7e58d35',
'segmentation_type': 'vlan',
'segmentation_id': 10}]
}
}
}
}
'''
gw_template = '''
{
'AWSTemplateFormatVersion': '2010-09-09',
@ -46,7 +71,7 @@ gw_template = '''
'id': 'e52148ca-7db9-4ec3-abe6-2c7c0ff316eb',
'interface_name': 'breth1'}],
'connections': [{
'network_id': '6af055d3-26f6-48dd-a597-7611d7e58d35',
'network': '6af055d3-26f6-48dd-a597-7611d7e58d35',
'segmentation_type': 'vlan',
'segmentation_id': 10}]
}
@ -85,9 +110,11 @@ class NeutronNetworkGatewayTest(HeatTestCase):
self.m.StubOutWithMock(neutronclient.Client,
'disconnect_network_gateway')
self.m.StubOutWithMock(neutronclient.Client, 'list_networks')
self.m.StubOutWithMock(neutron.neutronV20,
'find_resourceid_by_name_or_id')
self.m.StubOutWithMock(clients.OpenStackClients, 'keystone')
def prepare_create_network_gateway(self):
def prepare_create_network_gateway(self, resolve_neutron=True):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
neutronclient.Client.create_network_gateway({
@ -122,17 +149,35 @@ class NeutronNetworkGatewayTest(HeatTestCase):
'port_id': u'32acc49c-899e-44ea-8177-6f4157e12eb4'
}
})
if resolve_neutron:
neutron.neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'network',
'6af055d3-26f6-48dd-a597-7611d7e58d35'
).AndReturn('6af055d3-26f6-48dd-a597-7611d7e58d35')
t = template_format.parse(gw_template)
else:
t = template_format.parse(gw_template_deprecated)
t = template_format.parse(gw_template)
stack = utils.parse_stack(t)
rsrc = network_gateway.NetworkGateway(
'test_network_gateway',
t['Resources']['NetworkGateway'], stack)
return rsrc
def test_network_gateway_create(self):
rsrc = self.prepare_create_network_gateway()
def _test_network_gateway_create(self, resolve_neutron=True):
rsrc = self.prepare_create_network_gateway(resolve_neutron)
if resolve_neutron:
neutron.neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'network',
'6af055d3-26f6-48dd-a597-7611d7e58d35'
).AndReturn('6af055d3-26f6-48dd-a597-7611d7e58d35')
neutron.neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'network',
'6af055d3-26f6-48dd-a597-7611d7e58d35'
).AndReturn('6af055d3-26f6-48dd-a597-7611d7e58d35')
neutronclient.Client.disconnect_network_gateway(
'ed4c03b9-8251-4c09-acc4-e59ee9e6aa37', {
'network_id': u'6af055d3-26f6-48dd-a597-7611d7e58d35',
@ -185,8 +230,44 @@ class NeutronNetworkGatewayTest(HeatTestCase):
self.assertEqual((rsrc.DELETE, rsrc.COMPLETE), rsrc.state)
self.m.VerifyAll()
def test_network_gateway_create_deprecated(self):
self._test_network_gateway_create(resolve_neutron=False)
def test_network_gateway_create(self):
self._test_network_gateway_create()
def test_network_gateway_update(self):
rsrc = self.prepare_create_network_gateway()
neutron.neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'network',
'6af055d3-26f6-48dd-a597-7611d7e58d35'
).AndReturn('6af055d3-26f6-48dd-a597-7611d7e58d35')
neutron.neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'network',
'6af055d3-26f6-48dd-a597-7611d7e58d35'
).AndReturn('6af055d3-26f6-48dd-a597-7611d7e58d35')
neutron.neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'network',
'6af055d3-26f6-48dd-a597-7611d7e58d35'
).AndReturn('6af055d3-26f6-48dd-a597-7611d7e58d35')
neutron.neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'network',
'6af055d3-26f6-48dd-a597-7611d7e58d35'
).AndReturn('6af055d3-26f6-48dd-a597-7611d7e58d35')
neutron.neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'network',
'6af055d3-26f6-48dd-a597-7611d7e58d35'
).AndReturn('6af055d3-26f6-48dd-a597-7611d7e58d35')
neutron.neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'network',
'6af055d3-26f6-48dd-a597-7611d7e58d35'
).AndReturn('6af055d3-26f6-48dd-a597-7611d7e58d35')
neutronclient.Client.update_network_gateway(
u'ed4c03b9-8251-4c09-acc4-e59ee9e6aa37', {
@ -301,7 +382,7 @@ class NeutronNetworkGatewayTest(HeatTestCase):
'id': u'e52148ca-7db9-4ec3-abe6-2c7c0ff316eb',
'interface_name': u'breth1'}],
'connections': [{
'network_id': '6af055d3-26f6-48dd-a597-7611d7e58d35',
'network': '6af055d3-26f6-48dd-a597-7611d7e58d35',
'segmentation_type': 'vlan',
'segmentation_id': 10}]
}
@ -319,14 +400,14 @@ class NeutronNetworkGatewayTest(HeatTestCase):
'id': u'e52148ca-7db9-4ec3-abe6-2c7c0ff316eb',
'interface_name': u'breth1'}],
'connections': [{
'network_id': u'6af055d3-26f6-48dd-a597-7611d7e58d35',
'network': u'6af055d3-26f6-48dd-a597-7611d7e58d35',
'segmentation_type': u'flat',
'segmentation_id': 0}]
}
}
prop_diff = {
'connections': [{
'network_id': u'6af055d3-26f6-48dd-a597-7611d7e58d35',
'network': u'6af055d3-26f6-48dd-a597-7611d7e58d35',
'segmentation_type': u'flat',
'segmentation_id': 0}]
}

View File

@ -12,6 +12,7 @@
# under the License.
import copy
import mox
from testtools import skipIf
@ -19,6 +20,7 @@ from heat.common import exception
from heat.common import template_format
from heat.engine import clients
from heat.engine.resources.neutron import vpnservice
from heat.engine.resources.neutron import neutron
from heat.engine import scheduler
from heat.openstack.common.importutils import try_import
from heat.tests.common import HeatTestCase
@ -28,6 +30,26 @@ from heat.tests import utils
neutronclient = try_import('neutronclient.v2_0.client')
vpnservice_template_deprecated = '''
{
"AWSTemplateFormatVersion" : "2010-09-09",
"Description" : "Template to test VPN service resource",
"Parameters" : {},
"Resources" : {
"VPNService" : {
"Type" : "OS::Neutron::VPNService",
"Properties" : {
"name" : "VPNService",
"description" : "My new VPN service",
"admin_state_up" : true,
"router_id" : "rou123",
"subnet_id" : "sub123"
}
}
}
}
'''
vpnservice_template = '''
{
"AWSTemplateFormatVersion" : "2010-09-09",
@ -41,7 +63,7 @@ vpnservice_template = '''
"description" : "My new VPN service",
"admin_state_up" : true,
"router_id" : "rou123",
"subnet_id" : "sub123"
"subnet" : "sub123"
}
}
}
@ -152,21 +174,38 @@ class VPNServiceTest(HeatTestCase):
self.m.StubOutWithMock(neutronclient.Client, 'delete_vpnservice')
self.m.StubOutWithMock(neutronclient.Client, 'show_vpnservice')
self.m.StubOutWithMock(neutronclient.Client, 'update_vpnservice')
self.m.StubOutWithMock(neutron.neutronV20,
'find_resourceid_by_name_or_id')
self.m.StubOutWithMock(clients.OpenStackClients, 'keystone')
def create_vpnservice(self):
def create_vpnservice(self, resolve_neutron=True):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
if resolve_neutron:
neutron.neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'subnet',
'sub123'
).AndReturn('sub123')
snippet = template_format.parse(vpnservice_template)
else:
snippet = template_format.parse(vpnservice_template_deprecated)
neutronclient.Client.create_vpnservice(
self.VPN_SERVICE_CONF).AndReturn({'vpnservice': {'id': 'vpn123'}})
snippet = template_format.parse(vpnservice_template)
self.stack = utils.parse_stack(snippet)
return vpnservice.VPNService('vpnservice',
snippet['Resources']['VPNService'],
self.stack)
def test_create_deprecated(self):
self._test_create(resolve_neutron=False)
def test_create(self):
rsrc = self.create_vpnservice()
self._test_create()
def _test_create(self, resolve_neutron=True):
rsrc = self.create_vpnservice(resolve_neutron)
self.m.ReplayAll()
scheduler.TaskRunner(rsrc.create)()
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
@ -175,6 +214,12 @@ class VPNServiceTest(HeatTestCase):
def test_create_failed(self):
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
neutron.neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'subnet',
'sub123'
).AndReturn('sub123')
neutronclient.Client.create_vpnservice(self.VPN_SERVICE_CONF).AndRaise(
vpnservice.NeutronClientException())
self.m.ReplayAll()