Add address format check for property
Some resource receive IP address as property, this patch adds custom constraint for IP address format validation. Implemented: blueprint enhance-property-constraints Change-Id: Ie4ad71418aa355a4e24ac6c2a33bd85c19c5ef11changes/88/173688/6
parent
0185ad16f4
commit
e7aeb452f4
|
@ -14,6 +14,7 @@
|
|||
from neutronclient.common import exceptions
|
||||
from neutronclient.neutron import v2_0 as neutronV20
|
||||
from neutronclient.v2_0 import client as nc
|
||||
from oslo_utils import netutils
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from heat.common import exception
|
||||
|
@ -172,3 +173,10 @@ class SubnetConstraint(constraints.BaseCustomConstraint):
|
|||
neutron_client = client.client('neutron')
|
||||
neutronV20.find_resourceid_by_name_or_id(
|
||||
neutron_client, 'subnet', value)
|
||||
|
||||
|
||||
class IPConstraint(constraints.BaseCustomConstraint):
|
||||
|
||||
def validate(self, value, context):
|
||||
self._error_message = 'Invalid IP address'
|
||||
return netutils.is_valid_ip(value)
|
||||
|
|
|
@ -201,7 +201,10 @@ class ElasticIpAssociation(resource.Resource):
|
|||
EIP: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('EIP address to associate with instance.'),
|
||||
update_allowed=True
|
||||
update_allowed=True,
|
||||
constraints=[
|
||||
constraints.CustomConstraint('ip_addr')
|
||||
]
|
||||
),
|
||||
ALLOCATION_ID: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
|
|
|
@ -78,13 +78,19 @@ class FloatingIP(neutron.NeutronResource):
|
|||
FIXED_IP_ADDRESS: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('IP address to use if the port has multiple addresses.'),
|
||||
update_allowed=True
|
||||
update_allowed=True,
|
||||
constraints=[
|
||||
constraints.CustomConstraint('ip_addr')
|
||||
]
|
||||
),
|
||||
FLOATING_IP_ADDRESS: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('IP address of the floating IP. NOTE: The default policy '
|
||||
'setting in Neutron restricts usage of this property to '
|
||||
'administrative users only.'),
|
||||
constraints=[
|
||||
constraints.CustomConstraint('ip_addr')
|
||||
],
|
||||
support_status=support.SupportStatus(version='2015.2'),
|
||||
),
|
||||
}
|
||||
|
@ -257,7 +263,10 @@ class FloatingIPAssociation(neutron.NeutronResource):
|
|||
FIXED_IP_ADDRESS: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('IP address to use if the port has multiple addresses.'),
|
||||
update_allowed=True
|
||||
update_allowed=True,
|
||||
constraints=[
|
||||
constraints.CustomConstraint('ip_addr')
|
||||
]
|
||||
),
|
||||
}
|
||||
|
||||
|
|
|
@ -278,7 +278,10 @@ class Pool(neutron.NeutronResource):
|
|||
),
|
||||
VIP_ADDRESS: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('IP address of the vip.')
|
||||
_('IP address of the vip.'),
|
||||
constraints=[
|
||||
constraints.CustomConstraint('ip_addr')
|
||||
]
|
||||
),
|
||||
VIP_CONNECTION_LIMIT: properties.Schema(
|
||||
properties.Schema.INTEGER,
|
||||
|
@ -547,7 +550,10 @@ class PoolMember(neutron.NeutronResource):
|
|||
ADDRESS: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('IP address of the pool member on the pool network.'),
|
||||
required=True
|
||||
required=True,
|
||||
constraints=[
|
||||
constraints.CustomConstraint('ip_addr')
|
||||
]
|
||||
),
|
||||
PROTOCOL_PORT: properties.Schema(
|
||||
properties.Schema.INTEGER,
|
||||
|
|
|
@ -132,7 +132,10 @@ class Port(neutron.NeutronResource):
|
|||
),
|
||||
FIXED_IP_IP_ADDRESS: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('IP address desired in the subnet for this port.')
|
||||
_('IP address desired in the subnet for this port.'),
|
||||
constraints=[
|
||||
constraints.CustomConstraint('ip_addr')
|
||||
]
|
||||
),
|
||||
},
|
||||
),
|
||||
|
@ -166,7 +169,10 @@ class Port(neutron.NeutronResource):
|
|||
ALLOWED_ADDRESS_PAIR_IP_ADDRESS: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('IP address to allow through this port.'),
|
||||
required=True
|
||||
required=True,
|
||||
constraints=[
|
||||
constraints.CustomConstraint('ip_addr')
|
||||
]
|
||||
),
|
||||
},
|
||||
)
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo_utils import netutils
|
||||
|
||||
from heat.common import exception
|
||||
from heat.common.i18n import _
|
||||
from heat.engine import attributes
|
||||
|
@ -130,11 +132,17 @@ class Subnet(neutron.NeutronResource):
|
|||
schema={
|
||||
ALLOCATION_POOL_START: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
required=True
|
||||
required=True,
|
||||
constraints=[
|
||||
constraints.CustomConstraint('ip_addr')
|
||||
]
|
||||
),
|
||||
ALLOCATION_POOL_END: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
required=True
|
||||
required=True,
|
||||
constraints=[
|
||||
constraints.CustomConstraint('ip_addr')
|
||||
]
|
||||
),
|
||||
},
|
||||
)
|
||||
|
@ -155,7 +163,10 @@ class Subnet(neutron.NeutronResource):
|
|||
),
|
||||
ROUTE_NEXTHOP: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
required=True
|
||||
required=True,
|
||||
constraints=[
|
||||
constraints.CustomConstraint('ip_addr')
|
||||
]
|
||||
),
|
||||
},
|
||||
),
|
||||
|
@ -247,6 +258,13 @@ class Subnet(neutron.NeutronResource):
|
|||
'they must be equal.')
|
||||
raise exception.StackValidationFailed(message=msg)
|
||||
|
||||
gateway_ip = self.properties.get(self.GATEWAY_IP)
|
||||
if (gateway_ip and gateway_ip not in ['~', ''] and
|
||||
not netutils.is_valid_ip(gateway_ip)):
|
||||
msg = (_('Gateway IP address "%(gateway)" is in '
|
||||
'invalid format.'), gateway_ip)
|
||||
raise exception.StackValidationFailed(message=msg)
|
||||
|
||||
def handle_create(self):
|
||||
props = self.prepare_properties(
|
||||
self.properties,
|
||||
|
|
|
@ -339,7 +339,10 @@ class Server(stack_user.StackUser):
|
|||
NETWORK_FIXED_IP: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('Fixed IP address to specify for the port '
|
||||
'created on the requested network.')
|
||||
'created on the requested network.'),
|
||||
constraints=[
|
||||
constraints.CustomConstraint('ip_addr')
|
||||
]
|
||||
),
|
||||
NETWORK_PORT: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
|
|
|
@ -149,7 +149,10 @@ class OSDBInstance(resource.Resource):
|
|||
),
|
||||
V4_FIXED_IP: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('Fixed IPv4 address for this NIC.')
|
||||
_('Fixed IPv4 address for this NIC.'),
|
||||
constraints=[
|
||||
constraints.CustomConstraint('ip_addr')
|
||||
]
|
||||
),
|
||||
},
|
||||
),
|
||||
|
|
|
@ -182,3 +182,50 @@ class NeutronConstraintsValidate(common.HeatTestCase):
|
|||
self.assertFalse(constraint.validate("bar", ctx))
|
||||
mock_find.assert_has_calls([mock.call(nc, self.resource_type, 'foo'),
|
||||
mock.call(nc, self.resource_type, 'bar')])
|
||||
|
||||
|
||||
class TestIPConstraint(common.HeatTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestIPConstraint, self).setUp()
|
||||
self.constraint = neutron.IPConstraint()
|
||||
|
||||
def test_validate_ipv4_format(self):
|
||||
validate_format = [
|
||||
'1.1.1.1',
|
||||
'1.0.1.1',
|
||||
'255.255.255.255'
|
||||
]
|
||||
for ip in validate_format:
|
||||
self.assertTrue(self.constraint.validate(ip, None))
|
||||
|
||||
def test_invalidate_ipv4_format(self):
|
||||
invalidate_format = [
|
||||
'1.1.1.',
|
||||
'1.1.1.256',
|
||||
'invalidate format',
|
||||
'1.a.1.1'
|
||||
]
|
||||
for ip in invalidate_format:
|
||||
self.assertFalse(self.constraint.validate(ip, None))
|
||||
|
||||
def test_validate_ipv6_format(self):
|
||||
validate_format = [
|
||||
'2002:2002::20c:29ff:fe7d:811a',
|
||||
'::1',
|
||||
'2002::',
|
||||
'2002::1',
|
||||
]
|
||||
for ip in validate_format:
|
||||
self.assertTrue(self.constraint.validate(ip, None))
|
||||
|
||||
def test_invalidate_ipv6_format(self):
|
||||
invalidate_format = [
|
||||
'2002::2001::1',
|
||||
'2002::g',
|
||||
'invalidate format',
|
||||
'2001::0::',
|
||||
'20c:29ff:fe7d:811a'
|
||||
]
|
||||
for ip in invalidate_format:
|
||||
self.assertFalse(self.constraint.validate(ip, None))
|
||||
|
|
|
@ -72,6 +72,7 @@ heat.constraints =
|
|||
cinder.vtype = heat.engine.clients.os.cinder:VolumeTypeConstraint
|
||||
sahara.image = heat.engine.clients.os.sahara:ImageConstraint
|
||||
trove.flavor = heat.engine.clients.os.trove:FlavorConstraint
|
||||
ip_addr = heat.engine.clients.os.neutron:IPConstraint
|
||||
|
||||
heat.stack_lifecycle_plugins =
|
||||
|
||||
|
|
Loading…
Reference in New Issue