Ignore property errors in implicit dependencies

Implicit dependencies are calculated before resources are validated.
Therefore, any error getting a property that occurs during processing on
implicit dependencies will be raised without any of the context of which
resource is in error. By ignoring such property errors in implicit
dependency calculations, we can defer the error handling until the
actual resource validation and thus give the user an error message they
can actually use.

Change-Id: If7e0e8fd46b1b06a29cdd89743635ed32a9392e7
Closes-Bug: #1708209
This commit is contained in:
Zane Bitter 2017-08-02 14:12:12 -04:00
parent a322a299c6
commit b50df6b1fc
6 changed files with 113 additions and 34 deletions

View File

@ -101,12 +101,20 @@ class VPCGatewayAttachment(resource.Resource):
default_client_name = 'neutron'
def _vpc_route_tables(self):
def _vpc_route_tables(self, ignore_errors=False):
for res in six.itervalues(self.stack):
if (res.has_interface('AWS::EC2::RouteTable') and
res.properties.get(route_table.RouteTable.VPC_ID) ==
self.properties.get(self.VPC_ID)):
yield res
if res.has_interface('AWS::EC2::RouteTable'):
try:
vpc_id = self.properties[self.VPC_ID]
rt_vpc_id = res.properties.get(
route_table.RouteTable.VPC_ID)
except (ValueError, TypeError):
if ignore_errors:
continue
else:
raise
if rt_vpc_id == vpc_id:
yield res
def add_dependencies(self, deps):
super(VPCGatewayAttachment, self).add_dependencies(deps)
@ -114,7 +122,9 @@ class VPCGatewayAttachment(resource.Resource):
# VpcId as this VpcId.
# All route tables must exist before gateway attachment
# as attachment happens to routers (not VPCs)
for route_tbl in self._vpc_route_tables():
# Properties errors will be caught later in validation,
# where we can report them in their proper context.
for route_tbl in self._vpc_route_tables(ignore_errors=True):
deps += (self, route_tbl)
def handle_create(self):

View File

@ -19,6 +19,7 @@ from heat.common.i18n import _
from heat.engine import constraints
from heat.engine import properties
from heat.engine.resources.openstack.neutron import neutron
from heat.engine.resources.openstack.neutron import router
from heat.engine import support
@ -65,16 +66,29 @@ class ExtraRoute(neutron.NeutronResource):
# depend on any RouterInterface in this template with the same
# router_id as this router_id
if resource.has_interface('OS::Neutron::RouterInterface'):
router_id = self.properties[self.ROUTER_ID]
dep_router_id = resource.properties['router']
try:
router_id = self.properties[self.ROUTER_ID]
dep_router_id = resource.properties.get(
router.RouterInterface.ROUTER)
except (ValueError, TypeError):
# Properties errors will be caught later in validation,
# where we can report them in their proper context.
continue
if dep_router_id == router_id:
deps += (self, resource)
# depend on any RouterGateway in this template with the same
# router_id as this router_id
elif (resource.has_interface('OS::Neutron::RouterGateway') and
resource.properties['router_id'] ==
self.properties['router_id']):
deps += (self, resource)
elif resource.has_interface('OS::Neutron::RouterGateway'):
try:
router_id = self.properties[self.ROUTER_ID]
dep_router_id = resource.properties.get(
router.RouterGateway.ROUTER_ID)
except (ValueError, TypeError):
# Properties errors will be caught later in validation,
# where we can report them in their proper context.
continue
if dep_router_id == router_id:
deps += (self, resource)
def handle_create(self):
router_id = self.properties.get(self.ROUTER_ID)

View File

@ -203,7 +203,12 @@ class FloatingIP(neutron.NeutronResource):
if not resource.has_interface('OS::Neutron::Port'):
return False
fixed_ips = resource.properties.get(port.Port.FIXED_IPS)
try:
fixed_ips = resource.properties.get(port.Port.FIXED_IPS)
except (ValueError, TypeError):
# Properties errors will be caught later in validation, where
# we can report them in their proper context.
return False
if not fixed_ips:
# During create we have only unresolved value for
# functions, so can not use None value for building
@ -214,15 +219,24 @@ class FloatingIP(neutron.NeutronResource):
if subnet is None:
return True
p_net = (resource.properties.get(port.Port.NETWORK) or
resource.properties.get(port.Port.NETWORK_ID))
try:
p_net = (resource.properties.get(port.Port.NETWORK) or
resource.properties.get(port.Port.NETWORK_ID))
except (ValueError, TypeError):
# Properties errors will be caught later in validation,
# where we can report them in their proper context.
return False
if p_net:
network = self.client().show_network(p_net)['network']
return subnet in network['subnets']
else:
for fixed_ip in resource.properties.get(
port.Port.FIXED_IPS):
try:
fixed_ips = resource.properties.get(port.Port.FIXED_IPS)
except (ValueError, TypeError):
# Properties errors will be caught later in validation,
# where we can report them in their proper context.
return False
for fixed_ip in fixed_ips:
port_subnet = (fixed_ip.get(port.Port.FIXED_IP_SUBNET) or
fixed_ip.get(port.Port.FIXED_IP_SUBNET_ID))
if subnet == port_subnet:
@ -244,10 +258,16 @@ class FloatingIP(neutron.NeutronResource):
# depend on any RouterGateway in this template with the same
# network_id as this floating_network_id
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[self.FLOATING_NETWORK]
try:
gateway_network = (
resource.properties.get(router.RouterGateway.NETWORK)
or resource.properties.get(
router.RouterGateway.NETWORK_ID))
floating_network = self.properties[self.FLOATING_NETWORK]
except (ValueError, TypeError):
# Properties errors will be caught later in validation,
# where we can report them in their proper context.
continue
if gateway_network == floating_network:
deps += (self, resource)
@ -260,12 +280,17 @@ class FloatingIP(neutron.NeutronResource):
# this template with the same network_id as this
# floating_network_id
elif resource.has_interface('OS::Neutron::Router'):
gateway = resource.properties.get(
router.Router.EXTERNAL_GATEWAY)
try:
gateway = resource.properties.get(
router.Router.EXTERNAL_GATEWAY)
floating_network = self.properties[self.FLOATING_NETWORK]
except (ValueError, TypeError):
# Properties errors will be caught later in validation,
# where we can report them in their proper context.
continue
if gateway:
gateway_network = gateway.get(
router.Router.EXTERNAL_GATEWAY_NETWORK)
floating_network = self.properties[self.FLOATING_NETWORK]
if gateway_network == floating_network:
deps += (self, resource)

View File

@ -422,8 +422,13 @@ class Port(neutron.NeutronResource):
# the ports in that network.
for res in six.itervalues(self.stack):
if res.has_interface('OS::Neutron::Subnet'):
dep_network = res.properties.get(subnet.Subnet.NETWORK)
network = self.properties[self.NETWORK]
try:
dep_network = res.properties.get(subnet.Subnet.NETWORK)
network = self.properties[self.NETWORK]
except (ValueError, TypeError):
# Properties errors will be caught later in validation,
# where we can report them in their proper context.
continue
if dep_network == network:
deps += (self, res)

View File

@ -265,7 +265,12 @@ class Router(neutron.NeutronResource):
external_gw_net = external_gw.get(self.EXTERNAL_GATEWAY_NETWORK)
for res in six.itervalues(self.stack):
if res.has_interface('OS::Neutron::Subnet'):
subnet_net = res.properties.get(subnet.Subnet.NETWORK)
try:
subnet_net = res.properties.get(subnet.Subnet.NETWORK)
except (ValueError, TypeError):
# Properties errors will be caught later in validation,
# where we can report them in their proper context.
continue
if subnet_net == external_gw_net:
deps += (self, res)
@ -633,16 +638,26 @@ class RouterGateway(neutron.NeutronResource):
# depend on any RouterInterface in this template with the same
# router_id as this router_id
if resource.has_interface('OS::Neutron::RouterInterface'):
dep_router_id = resource.properties[RouterInterface.ROUTER]
router_id = self.properties[self.ROUTER_ID]
try:
dep_router_id = resource.properties[RouterInterface.ROUTER]
router_id = self.properties[self.ROUTER_ID]
except (ValueError, TypeError):
# Properties errors will be caught later in validation,
# where we can report them in their proper context.
continue
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
if resource.has_interface('OS::Neutron::Subnet'):
dep_network = resource.properties[subnet.Subnet.NETWORK]
network = self.properties[self.NETWORK]
try:
dep_network = resource.properties[subnet.Subnet.NETWORK]
network = self.properties[self.NETWORK]
except (ValueError, TypeError):
# Properties errors will be caught later in validation,
# where we can report them in their proper context.
continue
if dep_network == network:
deps += (self, resource)

View File

@ -1157,12 +1157,22 @@ class Server(server_base.BaseServer, sh.SchedulerHintsMixin,
# It is not known which subnet a server might be assigned
# to so all subnets in a network should be created before
# the servers in that network.
nets = self.properties[self.NETWORKS]
try:
nets = self.properties[self.NETWORKS]
except (ValueError, TypeError):
# Properties errors will be caught later in validation,
# where we can report them in their proper context.
return
if not nets:
return
for res in six.itervalues(self.stack):
if res.has_interface('OS::Neutron::Subnet'):
subnet_net = res.properties.get(subnet.Subnet.NETWORK)
try:
subnet_net = res.properties.get(subnet.Subnet.NETWORK)
except (ValueError, TypeError):
# Properties errors will be caught later in validation,
# where we can report them in their proper context.
continue
# Be wary of the case where we do not know a subnet's
# network. If that's the case, be safe and add it as a
# dependency.