Merge "Documentation for Drydock Validate Design API"

This commit is contained in:
Bryan Strassner 2018-01-08 10:51:14 -05:00 committed by Gerrit Code Review
commit fb8cbfc67d
4 changed files with 69 additions and 44 deletions

View File

@ -73,3 +73,9 @@ object in details can be extended with additional fields as needed.
Once a POST containing the ``status`` field is made to a bootaction-id, that bootaction-id can no
longer be updated with status changes nor additional detailed status messages.
validatedesign API
------------------
The Validatedesign API is used for validating documents before they will be used by Drydock. See
:ref:`validatedesign` for more details on validating documents.

View File

@ -44,6 +44,7 @@ API Documentation
API
task
bootaction
validatedesign
Client Documentation
--------------------

View File

@ -0,0 +1,31 @@
.. _validatedesign:
Validate Design
===============
The DryDock Validation API is a set of logic checks that must be passed before any information from the YAMLs will be
processed by Drydock. These checks are performed synchronously and will return a message list with a success or
failures for each check.
Formatting
----------
This document can be POSTed to the Drydock validatedesign to validate a set of documents that have been
processed by Deckhand::
{
rel : "design",
href: "deckhand+https://{{deckhand_url}}/revisions/{{revision_id}}/rendered-documents",
type: "application/x-yaml"
}
v1.0
----
Validation Checks
^^^^^^^^^^^^^^^^^
These checks are meant to check the business logic of documents sent to the validatedesign API.
.. autoclass:: drydock_provisioner.orchestrator.validations.validator.Validator
:members:

View File

@ -56,7 +56,12 @@ class Validator():
@classmethod
def rational_network_bond(cls, site_design):
"""
Ensures that NetworkLink 'bonding' is rational
This check ensures that each NetworkLink has a rational bonding setup.
If the bonding mode is set to 'disabled' then it ensures that no other options are specified.
If the bonding mode it set to '802.3ad' then it ensures that the bonding up delay and the bonding down delay
are both greater then or equal to the mon rate.
If the bonding mode is set to active-backup or balanced-rr then it ensures that the bonding hash and the
bonding peer rate are both NOT defined.
"""
message_list = []
site_design = site_design.obj_to_simple()
@ -141,7 +146,8 @@ class Validator():
@classmethod
def network_trunking_rational(cls, site_design):
"""
Ensures that Network Trunking is Rational
This check ensures that for each NetworkLink if the allowed networks are greater then 1 trunking mode is
enabled. It also makes sure that if trunking mode is disabled then a default network is defined.
"""
message_list = []
@ -190,7 +196,8 @@ class Validator():
@classmethod
def storage_partitioning(cls, site_design):
"""
Checks storage partitioning
This checks that for each storage device a partition list OR volume group is defined. Also for each partition
list it ensures that a file system and partition volume group are not defined in the same partition.
"""
message_list = []
site_design = site_design.obj_to_simple()
@ -249,8 +256,8 @@ class Validator():
if volume_group.get('name') not in volume_group_check_list:
msg = ('Storage Partitioning Error: A volume group must be assigned to a storage device or '
'partition; volume group %s on BaremetalNode %s' %
(volume_group.get('name'), baremetal_node.get('name')))
'partition; volume group %s on BaremetalNode %s' % (volume_group.get('name'),
baremetal_node.get('name')))
message_list.append(
TaskStatusMessage(
@ -420,7 +427,9 @@ class Validator():
@classmethod
def no_duplicate_IPs_check(cls, site_design):
"""
Ensures that the same IP is not assigned to multiple baremetal nodes.
Ensures that the same IP is not assigned to multiple baremetal node definitions by checking each new IP against
the list of known IPs. If the IP is unique no error is thrown and the new IP will be added to the list to be
checked against in the future.
"""
found_ips = {} # Dictionary Format - IP address: BaremetalNode name
message_list = []
@ -430,9 +439,7 @@ class Validator():
if not baremetal_nodes_list:
msg = 'No BaremetalNodes Found.'
message_list.append(
TaskStatusMessage(
msg=msg, error=False, ctx_type='NA', ctx='NA'))
message_list.append(TaskStatusMessage(msg=msg, error=False, ctx_type='NA', ctx='NA'))
else:
for node in baremetal_nodes_list:
addressing_list = node.get('addressing', [])
@ -443,19 +450,14 @@ class Validator():
if address in found_ips and address is not None:
msg = ('Error! Duplicate IP Address Found: %s '
'is in use by both %s and %s.'
% (address, found_ips[address], node_name))
message_list.append(
TaskStatusMessage(
msg=msg, error=True, ctx_type='NA', ctx='NA'))
'is in use by both %s and %s.' % (address, found_ips[address], node_name))
message_list.append(TaskStatusMessage(msg=msg, error=True, ctx_type='NA', ctx='NA'))
elif address is not None:
found_ips[address] = node_name
if not message_list:
msg = 'No Duplicate IP Addresses.'
message_list.append(
TaskStatusMessage(
msg=msg, error=False, ctx_type='NA', ctx='NA'))
message_list.append(TaskStatusMessage(msg=msg, error=False, ctx_type='NA', ctx='NA'))
return message_list
@ -523,7 +525,8 @@ class Validator():
@classmethod
def ip_locality_check(cls, site_design):
"""
Ensures that IP addresses are within defined CIDR ranges.
Ensures that each IP addresses assigned to a baremetal node is within the defined CIDR for the network. Also
verifies that the gateway IP for each static route of a network is within that network's CIDR.
"""
network_dict = {} # Dictionary Format - network name: cidr
message_list = []
@ -534,9 +537,7 @@ class Validator():
if not network_list:
msg = 'No networks found.'
message_list.append(
TaskStatusMessage(
msg=msg, error=False, ctx_type='NA', ctx='NA'))
message_list.append(TaskStatusMessage(msg=msg, error=False, ctx_type='NA', ctx='NA'))
else:
for net in network_list:
name = net.get('name')
@ -552,23 +553,16 @@ class Validator():
if not gateway:
msg = 'No gateway found for route %s.' % routes
message_list.append(
TaskStatusMessage(
msg=msg, error=True, ctx_type='NA', ctx='NA'))
message_list.append(TaskStatusMessage(msg=msg, error=True, ctx_type='NA', ctx='NA'))
else:
ip = IPAddress(gateway)
if ip not in cidr_range:
msg = ('IP Locality Error: The gateway IP Address %s '
'is not within the defined CIDR: %s of %s.'
% (gateway, cidr, name))
message_list.append(
TaskStatusMessage(
msg=msg, error=True, ctx_type='NA', ctx='NA'))
'is not within the defined CIDR: %s of %s.' % (gateway, cidr, name))
message_list.append(TaskStatusMessage(msg=msg, error=True, ctx_type='NA', ctx='NA'))
if not baremetal_nodes_list:
msg = 'No baremetal_nodes found.'
message_list.append(
TaskStatusMessage(
msg=msg, error=False, ctx_type='NA', ctx='NA'))
message_list.append(TaskStatusMessage(msg=msg, error=False, ctx_type='NA', ctx='NA'))
else:
for node in baremetal_nodes_list:
addressing_list = node.get('addressing', [])
@ -582,23 +576,16 @@ class Validator():
if ip_address_network_name not in network_dict:
msg = 'IP Locality Error: %s is not a valid network.' \
% (ip_address_network_name)
message_list.append(
TaskStatusMessage(
msg=msg, error=True, ctx_type='NA', ctx='NA'))
message_list.append(TaskStatusMessage(msg=msg, error=True, ctx_type='NA', ctx='NA'))
else:
if IPAddress(address) not in IPNetwork(network_dict[ip_address_network_name]):
msg = ('IP Locality Error: The IP Address %s '
'is not within the defined CIDR: %s of %s .'
% (address, network_dict[ip_address_network_name],
ip_address_network_name))
message_list.append(
TaskStatusMessage(
msg=msg, error=True, ctx_type='NA', ctx='NA'))
'is not within the defined CIDR: %s of %s .' %
(address, network_dict[ip_address_network_name], ip_address_network_name))
message_list.append(TaskStatusMessage(msg=msg, error=True, ctx_type='NA', ctx='NA'))
if not message_list:
msg = 'IP Locality Success'
message_list.append(
TaskStatusMessage(
msg=msg, error=False, ctx_type='NA', ctx='NA'))
message_list.append(TaskStatusMessage(msg=msg, error=False, ctx_type='NA', ctx='NA'))
return message_list