Files
instack-undercloud/instack_undercloud/validator.py
Ben Nemec 9c6424df5d Validate vips when generating certificate too
When generate_service_certificate is True,
undercloud_service_certificate will not necessarily be set when it
is passed to validation.  We need to check if either value is set
when deciding whether to validate vips.

Unit tests for this behavior were missing as well, so those have
been added.

Another consideration for this change is that we have started
passing non-IP values to these vip parameters when configuring
ssl.  This is counterintuitive, but apparently works as intended
so let's just rename the parameters and handle both IPs and DNS
names for those values.

Change-Id: I53151d4f555d5d161a3e53ce5f022e3bf3b2ffbd
Closes-Bug: 1643655
2017-01-19 08:11:21 -07:00

141 lines
5.9 KiB
Python

# Copyright 2015 Red Hat Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import netaddr
class FailedValidation(Exception):
pass
def validate_config(params, error_callback):
"""Validate an undercloud configuration described by params
:param params: A dict containing all of the undercloud.conf option
names mapped to their proposed values.
:param error_callback: A callback function that should be used to handle
errors. The function must accept a single parameter, which will be
a string describing the error.
"""
local_params = dict(params)
_validate_value_formats(local_params, error_callback)
_validate_in_cidr(local_params, error_callback)
_validate_dhcp_range(local_params, error_callback)
_validate_inspection_range(local_params, error_callback)
_validate_no_overlap(local_params, error_callback)
_validate_ips(local_params, error_callback)
def _validate_ips(params, error_callback):
def is_ip(value, param_name):
try:
netaddr.IPAddress(value)
except netaddr.core.AddrFormatError:
error_callback(
'%s "%s" must be a valid IP address' % (param_name, value))
for ip in params['undercloud_nameservers']:
is_ip(ip, 'undercloud_nameservers')
def _validate_value_formats(params, error_callback):
"""Validate format of some values
Certain values have a specific format that must be maintained in order to
work properly. For example, local_ip must be in CIDR form, and the
hostname must be a FQDN.
"""
try:
local_ip = netaddr.IPNetwork(params['local_ip'])
if local_ip.prefixlen == 32:
raise netaddr.AddrFormatError('Invalid netmask')
except netaddr.core.AddrFormatError as e:
message = ('local_ip "%s" not valid: "%s" '
'Value must be in CIDR format.' %
(params['local_ip'], str(e)))
error_callback(message)
hostname = params['undercloud_hostname']
if hostname is not None and '.' not in hostname:
message = 'Hostname "%s" is not fully qualified.' % hostname
error_callback(message)
def _validate_in_cidr(params, error_callback):
cidr = netaddr.IPNetwork(params['network_cidr'])
def validate_addr_in_cidr(params, name, pretty_name=None, require_ip=True):
try:
if netaddr.IPAddress(params[name]) not in cidr:
message = ('%s "%s" not in defined CIDR "%s"' %
(pretty_name or name, params[name], cidr))
error_callback(message)
except netaddr.core.AddrFormatError:
if require_ip:
message = 'Invalid IP address: %s' % params[name]
error_callback(message)
params['just_local_ip'] = params['local_ip'].split('/')[0]
# undercloud.conf uses inspection_iprange, the configuration wizard
# tool passes the values separately.
if 'inspection_iprange' in params:
inspection_iprange = params['inspection_iprange'].split(',')
params['inspection_start'] = inspection_iprange[0]
params['inspection_end'] = inspection_iprange[1]
validate_addr_in_cidr(params, 'just_local_ip', 'local_ip')
validate_addr_in_cidr(params, 'network_gateway')
if (params['undercloud_service_certificate'] or
params['generate_service_certificate']):
validate_addr_in_cidr(params, 'undercloud_public_host',
require_ip=False)
validate_addr_in_cidr(params, 'undercloud_admin_host',
require_ip=False)
validate_addr_in_cidr(params, 'dhcp_start')
validate_addr_in_cidr(params, 'dhcp_end')
validate_addr_in_cidr(params, 'inspection_start', 'Inspection range start')
validate_addr_in_cidr(params, 'inspection_end', 'Inspection range end')
def _validate_dhcp_range(params, error_callback):
dhcp_start = netaddr.IPAddress(params['dhcp_start'])
dhcp_end = netaddr.IPAddress(params['dhcp_end'])
if dhcp_start >= dhcp_end:
message = ('Invalid dhcp range specified, dhcp_start "%s" does '
'not come before dhcp_end "%s"' %
(dhcp_start, dhcp_end))
error_callback(message)
def _validate_inspection_range(params, error_callback):
inspection_start = netaddr.IPAddress(params['inspection_start'])
inspection_end = netaddr.IPAddress(params['inspection_end'])
if inspection_start >= inspection_end:
message = ('Invalid inspection range specified, inspection_start '
'"%s" does not come before inspection_end "%s"' %
(inspection_start, inspection_end))
error_callback(message)
def _validate_no_overlap(params, error_callback):
"""Validate the provisioning and inspection ip ranges do not overlap"""
dhcp_set = netaddr.IPSet(netaddr.IPRange(params['dhcp_start'],
params['dhcp_end']))
inspection_set = netaddr.IPSet(netaddr.IPRange(params['inspection_start'],
params['inspection_end']))
# If there is any intersection of the two sets then we have a problem
if dhcp_set & inspection_set:
message = ('Inspection DHCP range "%s-%s" overlaps provisioning '
'DHCP range "%s-%s".' %
(params['inspection_start'], params['inspection_end'],
params['dhcp_start'], params['dhcp_end']))
error_callback(message)