Merge "Provides ability to reference existing neutron resources"
This commit is contained in:
commit
1aba2fa252
@ -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')
|
||||
|
@ -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'])
|
||||
|
@ -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']
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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):
|
||||
'''
|
||||
|
@ -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, []):
|
||||
|
@ -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})
|
||||
|
@ -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']
|
||||
|
@ -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'])
|
||||
|
@ -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',
|
||||
|
@ -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',
|
||||
|
@ -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}]
|
||||
}
|
||||
|
@ -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()
|
||||
|
Loading…
Reference in New Issue
Block a user