From 4bf2a1fad2f7e0d72a585da4adc49aa2ceea4906 Mon Sep 17 00:00:00 2001 From: Rabi Mishra Date: Fri, 28 Feb 2014 12:11:00 +0530 Subject: [PATCH] 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 --- .../extraroute/tests/test_extraroute.py | 4 +- heat/engine/resources/neutron/floatingip.py | 39 +- heat/engine/resources/neutron/loadbalancer.py | 30 +- .../resources/neutron/network_gateway.py | 34 +- heat/engine/resources/neutron/neutron.py | 37 ++ heat/engine/resources/neutron/port.py | 57 ++- heat/engine/resources/neutron/router.py | 105 ++-- heat/engine/resources/neutron/subnet.py | 22 +- heat/engine/resources/neutron/vpnservice.py | 27 +- heat/tests/test_neutron.py | 451 +++++++++++++++--- heat/tests/test_neutron_loadbalancer.py | 127 ++++- heat/tests/test_neutron_network_gateway.py | 99 +++- heat/tests/test_neutron_vpnservice.py | 53 +- 13 files changed, 895 insertions(+), 190 deletions(-) diff --git a/contrib/extraroute/extraroute/tests/test_extraroute.py b/contrib/extraroute/extraroute/tests/test_extraroute.py index 4cff232490..0c676a0369 100644 --- a/contrib/extraroute/extraroute/tests/test_extraroute.py +++ b/contrib/extraroute/extraroute/tests/test_extraroute.py @@ -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') diff --git a/heat/engine/resources/neutron/floatingip.py b/heat/engine/resources/neutron/floatingip.py index 8d3faf18dd..daccfd17a3 100644 --- a/heat/engine/resources/neutron/floatingip.py +++ b/heat/engine/resources/neutron/floatingip.py @@ -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']) diff --git a/heat/engine/resources/neutron/loadbalancer.py b/heat/engine/resources/neutron/loadbalancer.py index 3c3d4edd59..cd77fd8cde 100644 --- a/heat/engine/resources/neutron/loadbalancer.py +++ b/heat/engine/resources/neutron/loadbalancer.py @@ -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'] diff --git a/heat/engine/resources/neutron/network_gateway.py b/heat/engine/resources/neutron/network_gateway.py index b1bce9bfaa..174beb04fc 100644 --- a/heat/engine/resources/neutron/network_gateway.py +++ b/heat/engine/resources/neutron/network_gateway.py @@ -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 ) diff --git a/heat/engine/resources/neutron/neutron.py b/heat/engine/resources/neutron/neutron.py index ded6bb153d..8370302a9d 100644 --- a/heat/engine/resources/neutron/neutron.py +++ b/heat/engine/resources/neutron/neutron.py @@ -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): ''' diff --git a/heat/engine/resources/neutron/port.py b/heat/engine/resources/neutron/port.py index f210575cb2..74a9b0f125 100644 --- a/heat/engine/resources/neutron/port.py +++ b/heat/engine/resources/neutron/port.py @@ -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, []): diff --git a/heat/engine/resources/neutron/router.py b/heat/engine/resources/neutron/router.py index fd7ec281b0..6ae4fe89cf 100644 --- a/heat/engine/resources/neutron/router.py +++ b/heat/engine/resources/neutron/router.py @@ -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}) diff --git a/heat/engine/resources/neutron/subnet.py b/heat/engine/resources/neutron/subnet.py index 7ae6f1e280..25b2d177cf 100644 --- a/heat/engine/resources/neutron/subnet.py +++ b/heat/engine/resources/neutron/subnet.py @@ -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'] diff --git a/heat/engine/resources/neutron/vpnservice.py b/heat/engine/resources/neutron/vpnservice.py index 70a51047bc..6d6f964ae4 100644 --- a/heat/engine/resources/neutron/vpnservice.py +++ b/heat/engine/resources/neutron/vpnservice.py @@ -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']) diff --git a/heat/tests/test_neutron.py b/heat/tests/test_neutron.py index 48188070c9..22443bfc73 100644 --- a/heat/tests/test_neutron.py +++ b/heat/tests/test_neutron.py @@ -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', diff --git a/heat/tests/test_neutron_loadbalancer.py b/heat/tests/test_neutron_loadbalancer.py index 1c674faefe..ed7ec7abf6 100644 --- a/heat/tests/test_neutron_loadbalancer.py +++ b/heat/tests/test_neutron_loadbalancer.py @@ -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', diff --git a/heat/tests/test_neutron_network_gateway.py b/heat/tests/test_neutron_network_gateway.py index fdd780091b..bfd028c73a 100644 --- a/heat/tests/test_neutron_network_gateway.py +++ b/heat/tests/test_neutron_network_gateway.py @@ -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}] } diff --git a/heat/tests/test_neutron_vpnservice.py b/heat/tests/test_neutron_vpnservice.py index 691a447333..2c9d26ad5b 100644 --- a/heat/tests/test_neutron_vpnservice.py +++ b/heat/tests/test_neutron_vpnservice.py @@ -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()