Statemgmt access to build data
Add statemgmt methods for accessing build data tables. - Add post_build_data for saving new build data - Add get_build_data for selecting build data based on node name or task ID. - YAPF formatting that was wider scope than expected Change-Id: I415eb795e2e5cab6f6a4cc0dc90aa79497c41328
This commit is contained in:
parent
094eb7dd90
commit
a20ecbfba0
|
@ -18,7 +18,9 @@ from drydock_provisioner.statemgmt.db import tables
|
||||||
|
|
||||||
|
|
||||||
def upgrade():
|
def upgrade():
|
||||||
op.create_table(tables.BuildData.__tablename__, *tables.BuildData.__schema__)
|
op.create_table(tables.BuildData.__tablename__,
|
||||||
|
*tables.BuildData.__schema__)
|
||||||
|
|
||||||
|
|
||||||
def downgrade():
|
def downgrade():
|
||||||
op.drop_table(tables.BuildData.__tablename__)
|
op.drop_table(tables.BuildData.__tablename__)
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
"""Constants for the CLI and API client."""
|
"""Constants for the CLI and API client."""
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
|
|
||||||
class TaskStatus(Enum):
|
class TaskStatus(Enum):
|
||||||
Requested = 'requested'
|
Requested = 'requested'
|
||||||
Queued = 'queued'
|
Queued = 'queued'
|
||||||
|
|
|
@ -17,6 +17,7 @@ import time
|
||||||
from drydock_provisioner.cli.action import CliAction
|
from drydock_provisioner.cli.action import CliAction
|
||||||
from drydock_provisioner.cli.const import TaskStatus
|
from drydock_provisioner.cli.const import TaskStatus
|
||||||
|
|
||||||
|
|
||||||
class TaskList(CliAction): # pylint: disable=too-few-public-methods
|
class TaskList(CliAction): # pylint: disable=too-few-public-methods
|
||||||
"""Action to list tasks."""
|
"""Action to list tasks."""
|
||||||
|
|
||||||
|
@ -99,7 +100,8 @@ class TaskCreate(CliAction): # pylint: disable=too-few-public-methods
|
||||||
while True:
|
while True:
|
||||||
time.sleep(self.poll_interval)
|
time.sleep(self.poll_interval)
|
||||||
task = self.api_client.get_task(task_id=task_id)
|
task = self.api_client.get_task(task_id=task_id)
|
||||||
if task.get('status', '') in [TaskStatus.Complete, TaskStatus.Terminated]:
|
if task.get('status',
|
||||||
|
'') in [TaskStatus.Complete, TaskStatus.Terminated]:
|
||||||
return task
|
return task
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -55,34 +55,41 @@ def start_api(state_manager=None, ingester=None, orchestrator=None):
|
||||||
v1_0_routes = [
|
v1_0_routes = [
|
||||||
# API for managing orchestrator tasks
|
# API for managing orchestrator tasks
|
||||||
('/health', HealthResource()),
|
('/health', HealthResource()),
|
||||||
('/tasks', TasksResource(
|
('/tasks',
|
||||||
state_manager=state_manager, orchestrator=orchestrator)),
|
TasksResource(state_manager=state_manager,
|
||||||
|
orchestrator=orchestrator)),
|
||||||
('/tasks/{task_id}', TaskResource(state_manager=state_manager)),
|
('/tasks/{task_id}', TaskResource(state_manager=state_manager)),
|
||||||
|
|
||||||
# API for managing site design data
|
# API for managing site design data
|
||||||
('/designs', DesignsResource(state_manager=state_manager)),
|
('/designs', DesignsResource(state_manager=state_manager)),
|
||||||
('/designs/{design_id}', DesignResource(
|
('/designs/{design_id}',
|
||||||
state_manager=state_manager, orchestrator=orchestrator)),
|
DesignResource(
|
||||||
('/designs/{design_id}/parts', DesignsPartsResource(
|
state_manager=state_manager, orchestrator=orchestrator)),
|
||||||
state_manager=state_manager, ingester=ingester)),
|
('/designs/{design_id}/parts',
|
||||||
|
DesignsPartsResource(state_manager=state_manager, ingester=ingester)),
|
||||||
('/designs/{design_id}/parts/{kind}',
|
('/designs/{design_id}/parts/{kind}',
|
||||||
DesignsPartsKindsResource(state_manager=state_manager)),
|
DesignsPartsKindsResource(state_manager=state_manager)),
|
||||||
('/designs/{design_id}/parts/{kind}/{name}', DesignsPartResource(
|
('/designs/{design_id}/parts/{kind}/{name}',
|
||||||
state_manager=state_manager, orchestrator=orchestrator)),
|
DesignsPartResource(
|
||||||
|
state_manager=state_manager, orchestrator=orchestrator)),
|
||||||
|
|
||||||
# API to list current MaaS nodes
|
# API to list current MaaS nodes
|
||||||
('/nodes', NodesResource()),
|
('/nodes', NodesResource()),
|
||||||
# API for nodes to discover their boot actions during curtin install
|
# API for nodes to discover their boot actions during curtin install
|
||||||
('/bootactions/nodes/{hostname}/units', BootactionUnitsResource(
|
('/bootactions/nodes/{hostname}/units',
|
||||||
state_manager=state_manager, orchestrator=orchestrator)),
|
BootactionUnitsResource(
|
||||||
('/bootactions/nodes/{hostname}/files', BootactionFilesResource(
|
state_manager=state_manager, orchestrator=orchestrator)),
|
||||||
state_manager=state_manager, orchestrator=orchestrator)),
|
('/bootactions/nodes/{hostname}/files',
|
||||||
('/bootactions/{action_id}', BootactionResource(
|
BootactionFilesResource(
|
||||||
state_manager=state_manager, orchestrator=orchestrator)),
|
state_manager=state_manager, orchestrator=orchestrator)),
|
||||||
|
('/bootactions/{action_id}',
|
||||||
|
BootactionResource(
|
||||||
|
state_manager=state_manager, orchestrator=orchestrator)),
|
||||||
|
|
||||||
# API to validate schemas
|
# API to validate schemas
|
||||||
('/validatedesign', ValidationResource(
|
('/validatedesign',
|
||||||
state_manager=state_manager, orchestrator=orchestrator)),
|
ValidationResource(
|
||||||
|
state_manager=state_manager, orchestrator=orchestrator)),
|
||||||
]
|
]
|
||||||
|
|
||||||
for path, res in v1_0_routes:
|
for path, res in v1_0_routes:
|
||||||
|
|
|
@ -45,8 +45,9 @@ class TasksResource(StatefulResource):
|
||||||
resp.body = json.dumps(task_list)
|
resp.body = json.dumps(task_list)
|
||||||
resp.status = falcon.HTTP_200
|
resp.status = falcon.HTTP_200
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
self.error(req.context, "Unknown error: %s\n%s" %
|
self.error(req.context,
|
||||||
(str(ex), traceback.format_exc()))
|
"Unknown error: %s\n%s" % (str(ex),
|
||||||
|
traceback.format_exc()))
|
||||||
self.return_error(
|
self.return_error(
|
||||||
resp, falcon.HTTP_500, message="Unknown error", retry=False)
|
resp, falcon.HTTP_500, message="Unknown error", retry=False)
|
||||||
|
|
||||||
|
@ -78,8 +79,9 @@ class TasksResource(StatefulResource):
|
||||||
else:
|
else:
|
||||||
supported_actions.get(action)(self, req, resp, json_data)
|
supported_actions.get(action)(self, req, resp, json_data)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
self.error(req.context, "Unknown error: %s\n%s" %
|
self.error(req.context,
|
||||||
(str(ex), traceback.format_exc()))
|
"Unknown error: %s\n%s" % (str(ex),
|
||||||
|
traceback.format_exc()))
|
||||||
self.return_error(
|
self.return_error(
|
||||||
resp, falcon.HTTP_500, message="Unknown error", retry=False)
|
resp, falcon.HTTP_500, message="Unknown error", retry=False)
|
||||||
|
|
||||||
|
|
|
@ -70,6 +70,10 @@ class InvalidAssetLocation(BootactionError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class BuildDataError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class DriverError(Exception):
|
class DriverError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ def register_all():
|
||||||
importlib.import_module('drydock_provisioner.objects.rack')
|
importlib.import_module('drydock_provisioner.objects.rack')
|
||||||
importlib.import_module('drydock_provisioner.objects.bootaction')
|
importlib.import_module('drydock_provisioner.objects.bootaction')
|
||||||
importlib.import_module('drydock_provisioner.objects.task')
|
importlib.import_module('drydock_provisioner.objects.task')
|
||||||
|
importlib.import_module('drydock_provisioner.objects.builddata')
|
||||||
|
|
||||||
|
|
||||||
# Utility class for calculating inheritance
|
# Utility class for calculating inheritance
|
||||||
|
|
|
@ -57,7 +57,11 @@ class BootAction(base.DrydockPersistentObject, base.DrydockObject):
|
||||||
def get_name(self):
|
def get_name(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
def render_assets(self, nodename, site_design, action_id, design_ref,
|
def render_assets(self,
|
||||||
|
nodename,
|
||||||
|
site_design,
|
||||||
|
action_id,
|
||||||
|
design_ref,
|
||||||
type_filter=None):
|
type_filter=None):
|
||||||
"""Render all of the assets in this bootaction.
|
"""Render all of the assets in this bootaction.
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,132 @@
|
||||||
|
# Copyright 2017 AT&T Intellectual Property. All other rights reserved.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
"""Models for representing build data."""
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from drydock_provisioner import objects
|
||||||
|
|
||||||
|
import drydock_provisioner.error as errors
|
||||||
|
|
||||||
|
|
||||||
|
class BuildData(object):
|
||||||
|
"""Build data
|
||||||
|
|
||||||
|
:param node_name: The name of the node the data was collected from.
|
||||||
|
:param task_id: The uuid.UUID ID of the task initiating the collection
|
||||||
|
:param collected_data: Date/time the data was collected
|
||||||
|
:param generator: String description of the source of data (e.g. ``lshw``)
|
||||||
|
:param data_format: String MIME-type of ``data_element``
|
||||||
|
:param data_element: Data to be saved, will be cast to ``str``
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self,
|
||||||
|
node_name=None,
|
||||||
|
task_id=None,
|
||||||
|
collected_date=None,
|
||||||
|
generator=None,
|
||||||
|
data_format=None,
|
||||||
|
data_element=None):
|
||||||
|
"""Initiator for BuildData."""
|
||||||
|
if not all((node_name, task_id, generator, data_format, data_element)):
|
||||||
|
raise ValueError("Required field missing.")
|
||||||
|
|
||||||
|
try:
|
||||||
|
if isinstance(data_element, bytes):
|
||||||
|
data_element = data_element.decode('utf-8')
|
||||||
|
elif not isinstance(data_element, str):
|
||||||
|
data_element = str(data_element)
|
||||||
|
except Exception as ex:
|
||||||
|
raise errors.BuildDataError(
|
||||||
|
"Error saving build data - data_element type %s could"
|
||||||
|
"not be cast to string." % str(type(data_element)))
|
||||||
|
|
||||||
|
self.node_name = node_name
|
||||||
|
self.task_id = task_id
|
||||||
|
self.collected_date = collected_date or datetime.utcnow()
|
||||||
|
self.generator = generator
|
||||||
|
self.data_format = data_format
|
||||||
|
self.data_element = data_element
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def obj_name(cls):
|
||||||
|
return cls.__name__
|
||||||
|
|
||||||
|
def to_db(self):
|
||||||
|
"""Convert this instance to a dictionary for use persisting to a db.
|
||||||
|
|
||||||
|
include_id=False can be used for doing an update where the primary key
|
||||||
|
of the table shouldn't included in the values set
|
||||||
|
|
||||||
|
:param include_id: Whether to include task_id in the dictionary
|
||||||
|
"""
|
||||||
|
_dict = {
|
||||||
|
'node_name':
|
||||||
|
self.node_name,
|
||||||
|
'task_id':
|
||||||
|
self.task_id.bytes,
|
||||||
|
'collected_date':
|
||||||
|
None if self.collected_date is None else str(self.collected_date),
|
||||||
|
'generator':
|
||||||
|
self.generator,
|
||||||
|
'data_format':
|
||||||
|
self.data_format,
|
||||||
|
'data_element':
|
||||||
|
self.data_element,
|
||||||
|
}
|
||||||
|
|
||||||
|
return _dict
|
||||||
|
|
||||||
|
def to_dict(self, verbosity=2):
|
||||||
|
"""Convert this instance to a dictionary.
|
||||||
|
|
||||||
|
Intended for use in JSON serialization
|
||||||
|
``verbosity`` of 1 omits the data_element
|
||||||
|
|
||||||
|
:param verbosity: integer of how verbose to make the result.
|
||||||
|
"""
|
||||||
|
_dict = {
|
||||||
|
'node_name':
|
||||||
|
self.node_name,
|
||||||
|
'task_id':
|
||||||
|
str(self.task_id),
|
||||||
|
'collected_date':
|
||||||
|
None if self.collected_date is None else str(self.collected_date),
|
||||||
|
'generator':
|
||||||
|
self.generator,
|
||||||
|
'data_format':
|
||||||
|
self.data_format,
|
||||||
|
}
|
||||||
|
|
||||||
|
if verbosity > 1:
|
||||||
|
_dict['data_element'] = self.data_element
|
||||||
|
|
||||||
|
return _dict
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_db(cls, d):
|
||||||
|
"""Create an instance from a DB-based dictionary.
|
||||||
|
|
||||||
|
:param d: Dictionary of instance data
|
||||||
|
"""
|
||||||
|
d['task_id'] = uuid.UUID(bytes=bytes(d.get('task_id')))
|
||||||
|
|
||||||
|
i = BuildData(**d)
|
||||||
|
|
||||||
|
return i
|
||||||
|
|
||||||
|
|
||||||
|
# Add BuildData to objects scope
|
||||||
|
setattr(objects, BuildData.obj_name(), BuildData)
|
|
@ -364,8 +364,8 @@ class HostVolumeGroup(base.DrydockObject):
|
||||||
for f in inheritable_field_list:
|
for f in inheritable_field_list:
|
||||||
setattr(p, f,
|
setattr(p, f,
|
||||||
objects.Utils.apply_field_inheritance(
|
objects.Utils.apply_field_inheritance(
|
||||||
getattr(j, f, None),
|
getattr(j, f, None), getattr(
|
||||||
getattr(i, f, None)))
|
i, f, None)))
|
||||||
|
|
||||||
p.partitions = HostPartitionList.from_basic_list(
|
p.partitions = HostPartitionList.from_basic_list(
|
||||||
HostPartition.merge_lists(
|
HostPartition.merge_lists(
|
||||||
|
@ -478,8 +478,8 @@ class HostStorageDevice(base.DrydockObject):
|
||||||
for f in inherit_field_list:
|
for f in inherit_field_list:
|
||||||
setattr(p, f,
|
setattr(p, f,
|
||||||
objects.Utils.apply_field_inheritance(
|
objects.Utils.apply_field_inheritance(
|
||||||
getattr(j, f, None),
|
getattr(j, f, None), getattr(
|
||||||
getattr(i, f, None)))
|
i, f, None)))
|
||||||
|
|
||||||
p.labels = objects.Utils.merge_dicts(
|
p.labels = objects.Utils.merge_dicts(
|
||||||
getattr(j, 'labels', None),
|
getattr(j, 'labels', None),
|
||||||
|
@ -625,8 +625,8 @@ class HostPartition(base.DrydockObject):
|
||||||
for f in inherit_field_list:
|
for f in inherit_field_list:
|
||||||
setattr(p, f,
|
setattr(p, f,
|
||||||
objects.Utils.apply_field_inheritance(
|
objects.Utils.apply_field_inheritance(
|
||||||
getattr(j, f, None),
|
getattr(j, f, None), getattr(
|
||||||
getattr(i, f, None)))
|
i, f, None)))
|
||||||
add = False
|
add = False
|
||||||
p.source = hd_fields.ModelSource.Compiled
|
p.source = hd_fields.ModelSource.Compiled
|
||||||
effective_list.append(p)
|
effective_list.append(p)
|
||||||
|
@ -752,8 +752,8 @@ class HostVolume(base.DrydockObject):
|
||||||
for f in inherit_field_list:
|
for f in inherit_field_list:
|
||||||
setattr(p, f,
|
setattr(p, f,
|
||||||
objects.Utils.apply_field_inheritance(
|
objects.Utils.apply_field_inheritance(
|
||||||
getattr(j, f, None),
|
getattr(j, f, None), getattr(
|
||||||
getattr(i, f, None)))
|
i, f, None)))
|
||||||
add = False
|
add = False
|
||||||
p.source = hd_fields.ModelSource.Compiled
|
p.source = hd_fields.ModelSource.Compiled
|
||||||
effective_list.append(p)
|
effective_list.append(p)
|
||||||
|
|
|
@ -625,8 +625,8 @@ class TaskStatusMessage(object):
|
||||||
:param d: dictionary of values
|
:param d: dictionary of values
|
||||||
"""
|
"""
|
||||||
i = TaskStatusMessage(
|
i = TaskStatusMessage(
|
||||||
d.get('message', None),
|
d.get('message', None), d.get('error'), d.get('context_type'),
|
||||||
d.get('error'), d.get('context_type'), d.get('context'))
|
d.get('context'))
|
||||||
if 'extra' in d:
|
if 'extra' in d:
|
||||||
i.extra = d.get('extra')
|
i.extra = d.get('extra')
|
||||||
i.ts = d.get('ts', None)
|
i.ts = d.get('ts', None)
|
||||||
|
|
|
@ -252,7 +252,9 @@ class Orchestrator(object):
|
||||||
for n in nodes or []:
|
for n in nodes or []:
|
||||||
n.compile_applied_model(site_design)
|
n.compile_applied_model(site_design)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
self.logger.debug("Model inheritance skipped, no node definitions in site design.")
|
self.logger.debug(
|
||||||
|
"Model inheritance skipped, no node definitions in site design."
|
||||||
|
)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -552,8 +554,8 @@ class Orchestrator(object):
|
||||||
(ba.name, nodename))
|
(ba.name, nodename))
|
||||||
action_id = ulid2.generate_binary_ulid()
|
action_id = ulid2.generate_binary_ulid()
|
||||||
self.state_manager.post_boot_action(
|
self.state_manager.post_boot_action(
|
||||||
nodename,
|
nodename, task.get_id(), identity_key, action_id,
|
||||||
task.get_id(), identity_key, action_id, ba.name)
|
ba.name)
|
||||||
else:
|
else:
|
||||||
self.logger.debug(
|
self.logger.debug(
|
||||||
"Boot action %s has disabled signaling." % ba.name)
|
"Boot action %s has disabled signaling." % ba.name)
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
import re
|
import re
|
||||||
import drydock_provisioner.error as errors
|
import drydock_provisioner.error as errors
|
||||||
|
|
||||||
|
|
||||||
class SimpleBytes():
|
class SimpleBytes():
|
||||||
def calulate_bytes(size_str):
|
def calulate_bytes(size_str):
|
||||||
"""
|
"""
|
||||||
|
@ -33,7 +34,8 @@ class SimpleBytes():
|
||||||
match = regex.match(size_str)
|
match = regex.match(size_str)
|
||||||
|
|
||||||
if not match:
|
if not match:
|
||||||
raise errors.InvalidSizeFormat("Invalid size string format: %s" % size_str)
|
raise errors.InvalidSizeFormat(
|
||||||
|
"Invalid size string format: %s" % size_str)
|
||||||
|
|
||||||
base_size = int(match.group(1))
|
base_size = int(match.group(1))
|
||||||
|
|
||||||
|
|
|
@ -235,8 +235,10 @@ class Validator():
|
||||||
|
|
||||||
# error if both are defined
|
# error if both are defined
|
||||||
if all([fstype, partition_volume_group]):
|
if all([fstype, partition_volume_group]):
|
||||||
msg = ('Storage Partitioning Error: Both a volume group AND file system cannot be '
|
msg = (
|
||||||
'defined in a sigle partition; on BaremetalNode %s' % baremetal_node.get('name'))
|
'Storage Partitioning Error: Both a volume group AND file system cannot be '
|
||||||
|
'defined in a sigle partition; on BaremetalNode %s'
|
||||||
|
% baremetal_node.get('name'))
|
||||||
|
|
||||||
message_list.append(
|
message_list.append(
|
||||||
TaskStatusMessage(
|
TaskStatusMessage(
|
||||||
|
@ -255,9 +257,10 @@ class Validator():
|
||||||
for volume_group in all_volume_groups:
|
for volume_group in all_volume_groups:
|
||||||
if volume_group.get('name') not in volume_group_check_list:
|
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 '
|
msg = (
|
||||||
'partition; volume group %s on BaremetalNode %s' % (volume_group.get('name'),
|
'Storage Partitioning Error: A volume group must be assigned to a storage device or '
|
||||||
baremetal_node.get('name')))
|
'partition; volume group %s on BaremetalNode %s' %
|
||||||
|
(volume_group.get('name'), baremetal_node.get('name')))
|
||||||
|
|
||||||
message_list.append(
|
message_list.append(
|
||||||
TaskStatusMessage(
|
TaskStatusMessage(
|
||||||
|
@ -308,9 +311,13 @@ class Validator():
|
||||||
]
|
]
|
||||||
|
|
||||||
for name in duplicated_names:
|
for name in duplicated_names:
|
||||||
msg = ('Unique Network Error: Allowed network %s duplicated on NetworkLink %s and NetworkLink '
|
msg = (
|
||||||
'%s' % (name, network_link_name, network_link_name_2))
|
'Unique Network Error: Allowed network %s duplicated on NetworkLink %s and NetworkLink '
|
||||||
message_list.append(TaskStatusMessage(msg=msg, error=True, ctx_type='NA', ctx='NA'))
|
'%s' % (name, network_link_name,
|
||||||
|
network_link_name_2))
|
||||||
|
message_list.append(
|
||||||
|
TaskStatusMessage(
|
||||||
|
msg=msg, error=True, ctx_type='NA', ctx='NA'))
|
||||||
|
|
||||||
if not message_list:
|
if not message_list:
|
||||||
message_list.append(
|
message_list.append(
|
||||||
|
@ -339,8 +346,11 @@ class Validator():
|
||||||
mtu = network_link.get('mtu')
|
mtu = network_link.get('mtu')
|
||||||
# check mtu > 1400 and < 64000
|
# check mtu > 1400 and < 64000
|
||||||
if mtu and (mtu < 1400 or mtu > 64000):
|
if mtu and (mtu < 1400 or mtu > 64000):
|
||||||
msg = 'Mtu Error: Mtu must be between 1400 and 64000; on Network Link %s.' % network_link.get('name')
|
msg = 'Mtu Error: Mtu must be between 1400 and 64000; on Network Link %s.' % network_link.get(
|
||||||
message_list.append(TaskStatusMessage(msg=msg, error=True, ctx_type='NA', ctx='NA'))
|
'name')
|
||||||
|
message_list.append(
|
||||||
|
TaskStatusMessage(
|
||||||
|
msg=msg, error=True, ctx_type='NA', ctx='NA'))
|
||||||
|
|
||||||
# add assigned network to dict with parent mtu
|
# add assigned network to dict with parent mtu
|
||||||
assigned_network = network_link.get('native_network')
|
assigned_network = network_link.get('native_network')
|
||||||
|
@ -351,19 +361,27 @@ class Validator():
|
||||||
|
|
||||||
# check mtu > 1400 and < 64000
|
# check mtu > 1400 and < 64000
|
||||||
if network_mtu and (network_mtu < 1400 or network_mtu > 64000):
|
if network_mtu and (network_mtu < 1400 or network_mtu > 64000):
|
||||||
msg = 'Mtu Error: Mtu must be between 1400 and 64000; on Network %s.' % network.get('name')
|
msg = 'Mtu Error: Mtu must be between 1400 and 64000; on Network %s.' % network.get(
|
||||||
message_list.append(TaskStatusMessage(msg=msg, error=True, ctx_type='NA', ctx='NA'))
|
'name')
|
||||||
|
message_list.append(
|
||||||
|
TaskStatusMessage(
|
||||||
|
msg=msg, error=True, ctx_type='NA', ctx='NA'))
|
||||||
|
|
||||||
name = network.get('name')
|
name = network.get('name')
|
||||||
parent_mtu = parent_mtu_check.get(name)
|
parent_mtu = parent_mtu_check.get(name)
|
||||||
if network_mtu and parent_mtu:
|
if network_mtu and parent_mtu:
|
||||||
# check to make sure mtu for network is <= parent network link
|
# check to make sure mtu for network is <= parent network link
|
||||||
if network_mtu > parent_mtu:
|
if network_mtu > parent_mtu:
|
||||||
msg = 'Mtu Error: Mtu must be <= the parent Network Link; for Network %s' % (network.get('name'))
|
msg = 'Mtu Error: Mtu must be <= the parent Network Link; for Network %s' % (
|
||||||
message_list.append(TaskStatusMessage(msg=msg, error=True, ctx_type='NA', ctx='NA'))
|
network.get('name'))
|
||||||
|
message_list.append(
|
||||||
|
TaskStatusMessage(
|
||||||
|
msg=msg, error=True, ctx_type='NA', ctx='NA'))
|
||||||
|
|
||||||
if not message_list:
|
if not message_list:
|
||||||
message_list.append(TaskStatusMessage(msg='Mtu', error=False, ctx_type='NA', ctx='NA'))
|
message_list.append(
|
||||||
|
TaskStatusMessage(
|
||||||
|
msg='Mtu', error=False, ctx_type='NA', ctx='NA'))
|
||||||
return message_list
|
return message_list
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -390,38 +408,64 @@ class Validator():
|
||||||
percent = size.split('%')
|
percent = size.split('%')
|
||||||
if len(percent) == 2:
|
if len(percent) == 2:
|
||||||
if int(percent[0]) < 0:
|
if int(percent[0]) < 0:
|
||||||
msg = ('Storage Sizing Error: Storage partition size is < 0 '
|
msg = (
|
||||||
'on Baremetal Node %s' % baremetal_node.get('name'))
|
'Storage Sizing Error: Storage partition size is < 0 '
|
||||||
message_list.append(TaskStatusMessage(msg=msg, error=True, ctx_type='NA', ctx='NA'))
|
'on Baremetal Node %s' %
|
||||||
|
baremetal_node.get('name'))
|
||||||
|
message_list.append(
|
||||||
|
TaskStatusMessage(
|
||||||
|
msg=msg,
|
||||||
|
error=True,
|
||||||
|
ctx_type='NA',
|
||||||
|
ctx='NA'))
|
||||||
|
|
||||||
partition_sum += int(percent[0])
|
partition_sum += int(percent[0])
|
||||||
|
|
||||||
if partition_sum > 99:
|
if partition_sum > 99:
|
||||||
msg = ('Storage Sizing Error: Storage partition size is greater than '
|
msg = (
|
||||||
'99 on Baremetal Node %s' % baremetal_node.get('name'))
|
'Storage Sizing Error: Storage partition size is greater than '
|
||||||
message_list.append(TaskStatusMessage(msg=msg, error=True, ctx_type='NA', ctx='NA'))
|
'99 on Baremetal Node %s' %
|
||||||
|
baremetal_node.get('name'))
|
||||||
|
message_list.append(
|
||||||
|
TaskStatusMessage(
|
||||||
|
msg=msg, error=True, ctx_type='NA', ctx='NA'))
|
||||||
|
|
||||||
volume_groups = baremetal_node.get('volume_groups', [])
|
volume_groups = baremetal_node.get('volume_groups', [])
|
||||||
volume_sum = 0
|
volume_sum = 0
|
||||||
for volume_group in volume_groups:
|
for volume_group in volume_groups:
|
||||||
logical_volume_list = volume_group.get('logical_volumes', [])
|
logical_volume_list = volume_group.get(
|
||||||
|
'logical_volumes', [])
|
||||||
for logical_volume in logical_volume_list:
|
for logical_volume in logical_volume_list:
|
||||||
size = logical_volume.get('size')
|
size = logical_volume.get('size')
|
||||||
percent = size.split('%')
|
percent = size.split('%')
|
||||||
if len(percent) == 2:
|
if len(percent) == 2:
|
||||||
if int(percent[0]) < 0:
|
if int(percent[0]) < 0:
|
||||||
msg = ('Storage Sizing Error: Storage volume size is < 0 '
|
msg = (
|
||||||
'on Baremetal Node %s' % baremetal_node.get('name'))
|
'Storage Sizing Error: Storage volume size is < 0 '
|
||||||
message_list.append(TaskStatusMessage(msg=msg, error=True, ctx_type='NA', ctx='NA'))
|
'on Baremetal Node %s' %
|
||||||
|
baremetal_node.get('name'))
|
||||||
|
message_list.append(
|
||||||
|
TaskStatusMessage(
|
||||||
|
msg=msg,
|
||||||
|
error=True,
|
||||||
|
ctx_type='NA',
|
||||||
|
ctx='NA'))
|
||||||
volume_sum += int(percent[0])
|
volume_sum += int(percent[0])
|
||||||
|
|
||||||
if volume_sum > 99:
|
if volume_sum > 99:
|
||||||
msg = ('Storage Sizing Error: Storage volume size is greater '
|
msg = (
|
||||||
'than 99 on Baremetal Node %s.' % baremetal_node.get('name'))
|
'Storage Sizing Error: Storage volume size is greater '
|
||||||
message_list.append(TaskStatusMessage(msg=msg, error=True, ctx_type='NA', ctx='NA'))
|
'than 99 on Baremetal Node %s.' %
|
||||||
|
baremetal_node.get('name'))
|
||||||
|
message_list.append(
|
||||||
|
TaskStatusMessage(
|
||||||
|
msg=msg, error=True, ctx_type='NA', ctx='NA'))
|
||||||
|
|
||||||
if not message_list:
|
if not message_list:
|
||||||
message_list.append(TaskStatusMessage(msg='Storage Sizing', error=False, ctx_type='NA', ctx='NA'))
|
message_list.append(
|
||||||
|
TaskStatusMessage(
|
||||||
|
msg='Storage Sizing', error=False, ctx_type='NA',
|
||||||
|
ctx='NA'))
|
||||||
return message_list
|
return message_list
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -450,8 +494,11 @@ class Validator():
|
||||||
|
|
||||||
if address in found_ips and address is not None:
|
if address in found_ips and address is not None:
|
||||||
msg = ('Error! Duplicate IP Address Found: %s '
|
msg = ('Error! Duplicate IP Address Found: %s '
|
||||||
'is in use by both %s and %s.' % (address, found_ips[address], node_name))
|
'is in use by both %s and %s.' %
|
||||||
message_list.append(TaskStatusMessage(msg=msg, error=True, ctx_type='NA', ctx='NA'))
|
(address, found_ips[address], node_name))
|
||||||
|
message_list.append(
|
||||||
|
TaskStatusMessage(
|
||||||
|
msg=msg, error=True, ctx_type='NA', ctx='NA'))
|
||||||
elif address is not None:
|
elif address is not None:
|
||||||
found_ips[address] = node_name
|
found_ips[address] = node_name
|
||||||
|
|
||||||
|
@ -488,13 +535,25 @@ class Validator():
|
||||||
root_set = True
|
root_set = True
|
||||||
# check if size < 20GB
|
# check if size < 20GB
|
||||||
if cal_size < 20 * BYTES_IN_GB:
|
if cal_size < 20 * BYTES_IN_GB:
|
||||||
msg = ('Boot Storage Error: Root volume must be > 20GB on BaremetalNode '
|
msg = (
|
||||||
'%s' % baremetal_node.get('name'))
|
'Boot Storage Error: Root volume must be > 20GB on BaremetalNode '
|
||||||
message_list.append(TaskStatusMessage(msg=msg, error=True, ctx_type='NA', ctx='NA'))
|
'%s' % baremetal_node.get('name'))
|
||||||
|
message_list.append(
|
||||||
|
TaskStatusMessage(
|
||||||
|
msg=msg,
|
||||||
|
error=True,
|
||||||
|
ctx_type='NA',
|
||||||
|
ctx='NA'))
|
||||||
except errors.InvalidSizeFormat as e:
|
except errors.InvalidSizeFormat as e:
|
||||||
msg = ('Boot Storage Error: Root volume has an invalid size format on BaremetalNode'
|
msg = (
|
||||||
'%s.' % baremetal_node.get('name'))
|
'Boot Storage Error: Root volume has an invalid size format on BaremetalNode'
|
||||||
message_list.append(TaskStatusMessage(msg=msg, error=True, ctx_type='NA', ctx='NA'))
|
'%s.' % baremetal_node.get('name'))
|
||||||
|
message_list.append(
|
||||||
|
TaskStatusMessage(
|
||||||
|
msg=msg,
|
||||||
|
error=True,
|
||||||
|
ctx_type='NA',
|
||||||
|
ctx='NA'))
|
||||||
|
|
||||||
# check make sure root has been defined and boot volume > 1GB
|
# check make sure root has been defined and boot volume > 1GB
|
||||||
if root_set and host_partition.get('name') == 'boot':
|
if root_set and host_partition.get('name') == 'boot':
|
||||||
|
@ -504,22 +563,39 @@ class Validator():
|
||||||
cal_size = SimpleBytes.calulate_bytes(size)
|
cal_size = SimpleBytes.calulate_bytes(size)
|
||||||
# check if size < 1GB
|
# check if size < 1GB
|
||||||
if cal_size < BYTES_IN_GB:
|
if cal_size < BYTES_IN_GB:
|
||||||
msg = ('Boot Storage Error: Boot volume must be > 1GB on BaremetalNode '
|
msg = (
|
||||||
'%s' % baremetal_node.get('name'))
|
'Boot Storage Error: Boot volume must be > 1GB on BaremetalNode '
|
||||||
message_list.append(TaskStatusMessage(msg=msg, error=True, ctx_type='NA', ctx='NA'))
|
'%s' % baremetal_node.get('name'))
|
||||||
|
message_list.append(
|
||||||
|
TaskStatusMessage(
|
||||||
|
msg=msg,
|
||||||
|
error=True,
|
||||||
|
ctx_type='NA',
|
||||||
|
ctx='NA'))
|
||||||
except errors.InvalidSizeFormat as e:
|
except errors.InvalidSizeFormat as e:
|
||||||
msg = ('Boot Storage Error: Boot volume has an invalid size format on BaremetalNode '
|
msg = (
|
||||||
'%s.' % baremetal_node.get('name'))
|
'Boot Storage Error: Boot volume has an invalid size format on BaremetalNode '
|
||||||
message_list.append(TaskStatusMessage(msg=msg, error=True, ctx_type='NA', ctx='NA'))
|
'%s.' % baremetal_node.get('name'))
|
||||||
|
message_list.append(
|
||||||
|
TaskStatusMessage(
|
||||||
|
msg=msg,
|
||||||
|
error=True,
|
||||||
|
ctx_type='NA',
|
||||||
|
ctx='NA'))
|
||||||
|
|
||||||
# This must be set
|
# This must be set
|
||||||
if not root_set:
|
if not root_set:
|
||||||
msg = ('Boot Storage Error: Root volume has to be set and must be > 20GB on BaremetalNode '
|
msg = (
|
||||||
'%s' % baremetal_node.get('name'))
|
'Boot Storage Error: Root volume has to be set and must be > 20GB on BaremetalNode '
|
||||||
message_list.append(TaskStatusMessage(msg=msg, error=True, ctx_type='NA', ctx='NA'))
|
'%s' % baremetal_node.get('name'))
|
||||||
|
message_list.append(
|
||||||
|
TaskStatusMessage(
|
||||||
|
msg=msg, error=True, ctx_type='NA', ctx='NA'))
|
||||||
|
|
||||||
if not message_list:
|
if not message_list:
|
||||||
message_list.append(TaskStatusMessage(msg='Boot Storage', error=False, ctx_type='NA', ctx='NA'))
|
message_list.append(
|
||||||
|
TaskStatusMessage(
|
||||||
|
msg='Boot Storage', error=False, ctx_type='NA', ctx='NA'))
|
||||||
return message_list
|
return message_list
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -553,13 +629,25 @@ class Validator():
|
||||||
|
|
||||||
if not gateway:
|
if not gateway:
|
||||||
msg = 'No gateway found for route %s.' % routes
|
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:
|
else:
|
||||||
ip = IPAddress(gateway)
|
ip = IPAddress(gateway)
|
||||||
if ip not in cidr_range:
|
if ip not in cidr_range:
|
||||||
msg = ('IP Locality Error: The gateway IP Address %s '
|
msg = (
|
||||||
'is not within the defined CIDR: %s of %s.' % (gateway, cidr, name))
|
'IP Locality Error: The gateway IP Address %s '
|
||||||
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:
|
if not baremetal_nodes_list:
|
||||||
msg = 'No baremetal_nodes found.'
|
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'))
|
||||||
|
@ -576,13 +664,27 @@ class Validator():
|
||||||
if ip_address_network_name not in network_dict:
|
if ip_address_network_name not in network_dict:
|
||||||
msg = 'IP Locality Error: %s is not a valid network.' \
|
msg = 'IP Locality Error: %s is not a valid network.' \
|
||||||
% (ip_address_network_name)
|
% (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:
|
else:
|
||||||
if IPAddress(address) not in IPNetwork(network_dict[ip_address_network_name]):
|
if IPAddress(address) not in IPNetwork(
|
||||||
msg = ('IP Locality Error: The IP Address %s '
|
network_dict[ip_address_network_name]):
|
||||||
'is not within the defined CIDR: %s of %s .' %
|
msg = (
|
||||||
(address, network_dict[ip_address_network_name], ip_address_network_name))
|
'IP Locality Error: The IP Address %s '
|
||||||
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:
|
if not message_list:
|
||||||
msg = 'IP Locality Success'
|
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'))
|
||||||
|
|
|
@ -121,6 +121,6 @@ class BuildData(ExtendTable):
|
||||||
Column('task_id', pg.BYTEA(16), index=True),
|
Column('task_id', pg.BYTEA(16), index=True),
|
||||||
Column('collected_date', DateTime),
|
Column('collected_date', DateTime),
|
||||||
Column('generator', String(256)),
|
Column('generator', String(256)),
|
||||||
Column('format', String(32)),
|
Column('data_format', String(32)),
|
||||||
Column('data_element', Text),
|
Column('data_element', Text),
|
||||||
]
|
]
|
||||||
|
|
|
@ -24,6 +24,7 @@ from sqlalchemy import MetaData
|
||||||
|
|
||||||
import drydock_provisioner.objects as objects
|
import drydock_provisioner.objects as objects
|
||||||
import drydock_provisioner.objects.fields as hd_fields
|
import drydock_provisioner.objects.fields as hd_fields
|
||||||
|
import drydock_provisioner.error as errors
|
||||||
|
|
||||||
from .db import tables
|
from .db import tables
|
||||||
|
|
||||||
|
@ -49,6 +50,7 @@ class DrydockState(object):
|
||||||
self.active_instance_tbl = tables.ActiveInstance(self.db_metadata)
|
self.active_instance_tbl = tables.ActiveInstance(self.db_metadata)
|
||||||
self.boot_action_tbl = tables.BootAction(self.db_metadata)
|
self.boot_action_tbl = tables.BootAction(self.db_metadata)
|
||||||
self.ba_status_tbl = tables.BootActionStatus(self.db_metadata)
|
self.ba_status_tbl = tables.BootActionStatus(self.db_metadata)
|
||||||
|
self.build_data_tbl = tables.BuildData(self.db_metadata)
|
||||||
return
|
return
|
||||||
|
|
||||||
def tabularasa(self):
|
def tabularasa(self):
|
||||||
|
@ -62,6 +64,7 @@ class DrydockState(object):
|
||||||
'active_instance',
|
'active_instance',
|
||||||
'boot_action',
|
'boot_action',
|
||||||
'boot_action_status',
|
'boot_action_status',
|
||||||
|
'build_data',
|
||||||
]
|
]
|
||||||
|
|
||||||
conn = self.db_engine.connect()
|
conn = self.db_engine.connect()
|
||||||
|
@ -585,3 +588,85 @@ class DrydockState(object):
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
self.logger.error(
|
self.logger.error(
|
||||||
"Error querying boot action %s" % action_id, exc_info=ex)
|
"Error querying boot action %s" % action_id, exc_info=ex)
|
||||||
|
|
||||||
|
def post_build_data(self, build_data):
|
||||||
|
"""Write a new build data element to the database.
|
||||||
|
|
||||||
|
:param build_data: objects.BuildData instance to write
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
with self.db_engine.connect() as conn:
|
||||||
|
query = self.build_data_tbl.insert().values(
|
||||||
|
**build_data.to_db())
|
||||||
|
conn.execute(query)
|
||||||
|
return True
|
||||||
|
except Exception as ex:
|
||||||
|
self.logger.error("Error saving build data.", exc_info=ex)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_build_data(self,
|
||||||
|
node_name=None,
|
||||||
|
task_id=None,
|
||||||
|
latest=False,
|
||||||
|
verbosity=2):
|
||||||
|
"""Retrieve build data from the database.
|
||||||
|
|
||||||
|
If ``node_name`` or ``task_id`` are defined, use them as
|
||||||
|
filters for the build_data retrieved. If ``task_id`` is not
|
||||||
|
defined, ``latest`` determines if all build data is returned,
|
||||||
|
or only the chronologically latest version for each generator
|
||||||
|
description.
|
||||||
|
|
||||||
|
:param node_name: String name of the node to filter on
|
||||||
|
:param task_id: uuid.UUID ID of the task to filter on
|
||||||
|
:param latest: boolean whether to return only the latest
|
||||||
|
version for each generator
|
||||||
|
:param verbosity: integer of how verbose the response should
|
||||||
|
be. 1 is summary, 2 includes the collected data
|
||||||
|
:returns: list of objects.BuildData instances
|
||||||
|
"""
|
||||||
|
# TODO(sh8121att) possibly optimize queries by changing select column
|
||||||
|
# list based on verbosity
|
||||||
|
try:
|
||||||
|
with self.db_engine.connect() as conn:
|
||||||
|
if node_name and task_id:
|
||||||
|
query = self.build_data_tbl.select().where(
|
||||||
|
self.build_data_tbl.c.node_name == node_name
|
||||||
|
and self.build_data_tbl.c.task_id == task_id.bytes
|
||||||
|
).order_by(self.build_data_tbl.c.collected_date.desc())
|
||||||
|
rs = conn.execute(query)
|
||||||
|
elif node_name:
|
||||||
|
if latest:
|
||||||
|
query = sql.text(
|
||||||
|
'SELECT DISTINCT ON (generator) build_data.* '
|
||||||
|
'FROM build_data '
|
||||||
|
'WHERE build_data.node_name = :nodename '
|
||||||
|
'ORDER BY generator, build_data.collected_date DESC'
|
||||||
|
)
|
||||||
|
rs = conn.execute(query, nodename=node_name)
|
||||||
|
else:
|
||||||
|
query = self.build_data_tbl.select().where(
|
||||||
|
self.build_data_tbl.c.node_name == node_name)
|
||||||
|
rs = conn.execute(query)
|
||||||
|
elif task_id:
|
||||||
|
query = self.build_data_tbl.select().where(
|
||||||
|
self.build_data_tbl.c.task_id == task_id.bytes)
|
||||||
|
rs = conn.execute(query)
|
||||||
|
else:
|
||||||
|
if latest:
|
||||||
|
query = sql.text(
|
||||||
|
'SELECT DISTINCT ON (generator), build_data.* '
|
||||||
|
'FROM build_data '
|
||||||
|
'ORDER BY generator, build_data.collected.date DESC'
|
||||||
|
)
|
||||||
|
rs = conn.execute(query)
|
||||||
|
else:
|
||||||
|
query = self.build_data_tbl.select()
|
||||||
|
rs = conn.execute(query)
|
||||||
|
|
||||||
|
result_data = rs.fetchall()
|
||||||
|
|
||||||
|
return [objects.BuildData.from_db(dict(r)) for r in result_data]
|
||||||
|
except Exception as ex:
|
||||||
|
self.logger.error("Error selecting build data.", exc_info=ex)
|
||||||
|
raise errors.BuildDataError("Error selecting build data.")
|
||||||
|
|
|
@ -103,9 +103,8 @@ class TestClass(object):
|
||||||
|
|
||||||
id_key = os.urandom(32)
|
id_key = os.urandom(32)
|
||||||
action_id = ulid2.generate_binary_ulid()
|
action_id = ulid2.generate_binary_ulid()
|
||||||
blank_state.post_boot_action('compute01',
|
blank_state.post_boot_action('compute01', test_task.get_id(), id_key,
|
||||||
test_task.get_id(), id_key, action_id,
|
action_id, 'helloworld')
|
||||||
'helloworld')
|
|
||||||
|
|
||||||
ba = dict(
|
ba = dict(
|
||||||
nodename='compute01',
|
nodename='compute01',
|
||||||
|
|
|
@ -15,15 +15,16 @@
|
||||||
|
|
||||||
from drydock_provisioner.objects import fields as hd_fields
|
from drydock_provisioner.objects import fields as hd_fields
|
||||||
|
|
||||||
|
|
||||||
class TestBootActionSignal(object):
|
class TestBootActionSignal(object):
|
||||||
def test_bootaction_signal_disable(self, deckhand_orchestrator, drydock_state, input_files):
|
def test_bootaction_signal_disable(self, deckhand_orchestrator,
|
||||||
|
drydock_state, input_files):
|
||||||
"""Test that disabled signaling omits a status entry in the DB."""
|
"""Test that disabled signaling omits a status entry in the DB."""
|
||||||
input_file = input_files.join("deckhand_fullsite.yaml")
|
input_file = input_files.join("deckhand_fullsite.yaml")
|
||||||
design_ref = "file://%s" % str(input_file)
|
design_ref = "file://%s" % str(input_file)
|
||||||
|
|
||||||
task = deckhand_orchestrator.create_task(
|
task = deckhand_orchestrator.create_task(
|
||||||
design_ref=design_ref,
|
design_ref=design_ref, action=hd_fields.OrchestratorAction.Noop)
|
||||||
action=hd_fields.OrchestratorAction.Noop)
|
|
||||||
|
|
||||||
deckhand_orchestrator.create_bootaction_context("compute01", task)
|
deckhand_orchestrator.create_bootaction_context("compute01", task)
|
||||||
|
|
||||||
|
|
|
@ -20,15 +20,15 @@ import drydock_provisioner.objects.fields as hd_fields
|
||||||
|
|
||||||
|
|
||||||
class TestClass(object):
|
class TestClass(object):
|
||||||
def test_task_complete(self, yaml_ingester, input_files, setup, blank_state):
|
def test_task_complete(self, yaml_ingester, input_files, setup,
|
||||||
|
blank_state):
|
||||||
input_file = input_files.join("fullsite.yaml")
|
input_file = input_files.join("fullsite.yaml")
|
||||||
design_ref = "file://%s" % str(input_file)
|
design_ref = "file://%s" % str(input_file)
|
||||||
|
|
||||||
orchestrator = orch.Orchestrator(
|
orchestrator = orch.Orchestrator(
|
||||||
state_manager=blank_state, ingester=yaml_ingester)
|
state_manager=blank_state, ingester=yaml_ingester)
|
||||||
orch_task = orchestrator.create_task(
|
orch_task = orchestrator.create_task(
|
||||||
action=hd_fields.OrchestratorAction.Noop,
|
action=hd_fields.OrchestratorAction.Noop, design_ref=design_ref)
|
||||||
design_ref=design_ref)
|
|
||||||
orch_task.set_status(hd_fields.TaskStatus.Queued)
|
orch_task.set_status(hd_fields.TaskStatus.Queued)
|
||||||
orch_task.save()
|
orch_task.save()
|
||||||
|
|
||||||
|
@ -45,15 +45,15 @@ class TestClass(object):
|
||||||
orchestrator.stop_orchestrator()
|
orchestrator.stop_orchestrator()
|
||||||
orch_thread.join(10)
|
orch_thread.join(10)
|
||||||
|
|
||||||
def test_task_termination(self, input_files, yaml_ingester, setup, blank_state):
|
def test_task_termination(self, input_files, yaml_ingester, setup,
|
||||||
|
blank_state):
|
||||||
input_file = input_files.join("fullsite.yaml")
|
input_file = input_files.join("fullsite.yaml")
|
||||||
design_ref = "file://%s" % str(input_file)
|
design_ref = "file://%s" % str(input_file)
|
||||||
|
|
||||||
orchestrator = orch.Orchestrator(
|
orchestrator = orch.Orchestrator(
|
||||||
state_manager=blank_state, ingester=yaml_ingester)
|
state_manager=blank_state, ingester=yaml_ingester)
|
||||||
orch_task = orchestrator.create_task(
|
orch_task = orchestrator.create_task(
|
||||||
action=hd_fields.OrchestratorAction.Noop,
|
action=hd_fields.OrchestratorAction.Noop, design_ref=design_ref)
|
||||||
design_ref=design_ref)
|
|
||||||
|
|
||||||
orch_task.set_status(hd_fields.TaskStatus.Queued)
|
orch_task.set_status(hd_fields.TaskStatus.Queued)
|
||||||
orch_task.save()
|
orch_task.save()
|
||||||
|
|
|
@ -26,9 +26,8 @@ class TestPostgresBootAction(object):
|
||||||
id_key = os.urandom(32)
|
id_key = os.urandom(32)
|
||||||
action_id = ulid2.generate_binary_ulid()
|
action_id = ulid2.generate_binary_ulid()
|
||||||
nodename = 'testnode'
|
nodename = 'testnode'
|
||||||
result = drydock_state.post_boot_action(nodename,
|
result = drydock_state.post_boot_action(
|
||||||
populateddb.get_id(), id_key,
|
nodename, populateddb.get_id(), id_key, action_id, 'helloworld')
|
||||||
action_id, 'helloworld')
|
|
||||||
|
|
||||||
assert result
|
assert result
|
||||||
|
|
||||||
|
@ -37,9 +36,8 @@ class TestPostgresBootAction(object):
|
||||||
id_key = os.urandom(32)
|
id_key = os.urandom(32)
|
||||||
action_id = ulid2.generate_binary_ulid()
|
action_id = ulid2.generate_binary_ulid()
|
||||||
nodename = 'testnode'
|
nodename = 'testnode'
|
||||||
drydock_state.post_boot_action(nodename,
|
drydock_state.post_boot_action(nodename, populateddb.get_id(), id_key,
|
||||||
populateddb.get_id(), id_key, action_id,
|
action_id, 'helloworld')
|
||||||
'helloworld')
|
|
||||||
|
|
||||||
result = drydock_state.put_bootaction_status(
|
result = drydock_state.put_bootaction_status(
|
||||||
ulid2.encode_ulid_base32(action_id),
|
ulid2.encode_ulid_base32(action_id),
|
||||||
|
@ -52,9 +50,8 @@ class TestPostgresBootAction(object):
|
||||||
id_key = os.urandom(32)
|
id_key = os.urandom(32)
|
||||||
action_id = ulid2.generate_binary_ulid()
|
action_id = ulid2.generate_binary_ulid()
|
||||||
nodename = 'testnode'
|
nodename = 'testnode'
|
||||||
drydock_state.post_boot_action(nodename,
|
drydock_state.post_boot_action(nodename, populateddb.get_id(), id_key,
|
||||||
populateddb.get_id(), id_key, action_id,
|
action_id, 'helloworld')
|
||||||
'helloworld')
|
|
||||||
|
|
||||||
ba = drydock_state.get_boot_action(ulid2.encode_ulid_base32(action_id))
|
ba = drydock_state.get_boot_action(ulid2.encode_ulid_base32(action_id))
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,113 @@
|
||||||
|
# Copyright 2017 AT&T Intellectual Property. All other rights reserved.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
"""Test postgres integration for build data management."""
|
||||||
|
|
||||||
|
import uuid
|
||||||
|
import copy
|
||||||
|
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
from drydock_provisioner import objects
|
||||||
|
|
||||||
|
|
||||||
|
class TestBuildData(object):
|
||||||
|
def test_build_data_insert_no_collected_date(self, blank_state):
|
||||||
|
"""Test that build data can be inserted omitting collection date."""
|
||||||
|
build_data_fields = {
|
||||||
|
'node_name': 'foo',
|
||||||
|
'generator': 'hello_world',
|
||||||
|
'data_format': 'text/plain',
|
||||||
|
'data_element': 'Hello World!',
|
||||||
|
'task_id': uuid.uuid4(),
|
||||||
|
}
|
||||||
|
|
||||||
|
build_data = objects.BuildData(**build_data_fields)
|
||||||
|
|
||||||
|
result = blank_state.post_build_data(build_data)
|
||||||
|
|
||||||
|
assert result
|
||||||
|
|
||||||
|
def test_build_data_insert_iwth_collected_date(self, blank_state):
|
||||||
|
"""Test that build data can be inserted specifying collection date."""
|
||||||
|
build_data_fields = {
|
||||||
|
'node_name': 'foo',
|
||||||
|
'generator': 'hello_world',
|
||||||
|
'data_format': 'text/plain',
|
||||||
|
'data_element': 'Hello World!',
|
||||||
|
'task_id': uuid.uuid4(),
|
||||||
|
'collected_date': datetime.utcnow(),
|
||||||
|
}
|
||||||
|
|
||||||
|
build_data = objects.BuildData(**build_data_fields)
|
||||||
|
|
||||||
|
result = blank_state.post_build_data(build_data)
|
||||||
|
|
||||||
|
assert result
|
||||||
|
|
||||||
|
def test_build_data_select(self, blank_state):
|
||||||
|
"""Test that build data can be deserialized from the database."""
|
||||||
|
build_data_fields = {
|
||||||
|
'node_name': 'foo',
|
||||||
|
'generator': 'hello_world',
|
||||||
|
'data_format': 'text/plain',
|
||||||
|
'data_element': 'Hello World!',
|
||||||
|
'task_id': uuid.uuid4(),
|
||||||
|
'collected_date': datetime.utcnow(),
|
||||||
|
}
|
||||||
|
|
||||||
|
build_data = objects.BuildData(**build_data_fields)
|
||||||
|
|
||||||
|
result = blank_state.post_build_data(build_data)
|
||||||
|
|
||||||
|
assert result
|
||||||
|
|
||||||
|
bd_list = blank_state.get_build_data()
|
||||||
|
|
||||||
|
assert len(bd_list) == 1
|
||||||
|
|
||||||
|
assert bd_list[0].to_dict() == build_data.to_dict()
|
||||||
|
|
||||||
|
def test_build_data_select_latest(self, blank_state):
|
||||||
|
"""Test that build data can be selected for only latest instance."""
|
||||||
|
build_data_latest = {
|
||||||
|
'node_name': 'foo',
|
||||||
|
'generator': 'hello_world',
|
||||||
|
'data_format': 'text/plain',
|
||||||
|
'data_element': 'Hello World!',
|
||||||
|
'task_id': uuid.uuid4(),
|
||||||
|
'collected_date': datetime.utcnow(),
|
||||||
|
}
|
||||||
|
|
||||||
|
build_data_old = copy.deepcopy(build_data_latest)
|
||||||
|
build_data_old[
|
||||||
|
'collected_date'] = build_data_latest['collected_date'] - timedelta(
|
||||||
|
days=1)
|
||||||
|
build_data_old['task_id'] = uuid.uuid4()
|
||||||
|
|
||||||
|
build_data1 = objects.BuildData(**build_data_latest)
|
||||||
|
build_data2 = objects.BuildData(**build_data_old)
|
||||||
|
|
||||||
|
result = blank_state.post_build_data(build_data1)
|
||||||
|
|
||||||
|
assert result
|
||||||
|
|
||||||
|
result = blank_state.post_build_data(build_data2)
|
||||||
|
|
||||||
|
assert result
|
||||||
|
|
||||||
|
bd_list = blank_state.get_build_data(node_name='foo', latest=True)
|
||||||
|
|
||||||
|
assert len(bd_list) == 1
|
||||||
|
|
||||||
|
assert bd_list[0].to_dict() == build_data1.to_dict()
|
|
@ -20,7 +20,8 @@ import drydock_provisioner.objects as objects
|
||||||
|
|
||||||
|
|
||||||
class TestClass(object):
|
class TestClass(object):
|
||||||
def test_bootaction_render_nodename(self, input_files, deckhand_ingester, setup):
|
def test_bootaction_render_nodename(self, input_files, deckhand_ingester,
|
||||||
|
setup):
|
||||||
"""Test the bootaction render routine provides expected output."""
|
"""Test the bootaction render routine provides expected output."""
|
||||||
objects.register_all()
|
objects.register_all()
|
||||||
|
|
||||||
|
@ -34,11 +35,13 @@ class TestClass(object):
|
||||||
|
|
||||||
ba = design_data.get_bootaction('helloworld')
|
ba = design_data.get_bootaction('helloworld')
|
||||||
action_id = ulid2.generate_binary_ulid()
|
action_id = ulid2.generate_binary_ulid()
|
||||||
assets = ba.render_assets('compute01', design_data, action_id, design_ref)
|
assets = ba.render_assets('compute01', design_data, action_id,
|
||||||
|
design_ref)
|
||||||
|
|
||||||
assert 'compute01' in assets[0].rendered_bytes.decode('utf-8')
|
assert 'compute01' in assets[0].rendered_bytes.decode('utf-8')
|
||||||
|
|
||||||
def test_bootaction_render_design_ref(self, input_files, deckhand_ingester, setup):
|
def test_bootaction_render_design_ref(self, input_files, deckhand_ingester,
|
||||||
|
setup):
|
||||||
"""Test the bootaction render routine provides expected output."""
|
"""Test the bootaction render routine provides expected output."""
|
||||||
objects.register_all()
|
objects.register_all()
|
||||||
|
|
||||||
|
@ -52,6 +55,8 @@ class TestClass(object):
|
||||||
|
|
||||||
ba = design_data.get_bootaction('helloworld')
|
ba = design_data.get_bootaction('helloworld')
|
||||||
action_id = ulid2.generate_binary_ulid()
|
action_id = ulid2.generate_binary_ulid()
|
||||||
assets = ba.render_assets('compute01', design_data, action_id, design_ref)
|
assets = ba.render_assets('compute01', design_data, action_id,
|
||||||
|
design_ref)
|
||||||
|
|
||||||
assert 'deckhand_fullsite.yaml' in assets[2].rendered_bytes.decode('utf-8')
|
assert 'deckhand_fullsite.yaml' in assets[2].rendered_bytes.decode(
|
||||||
|
'utf-8')
|
||||||
|
|
|
@ -39,7 +39,8 @@ class TestClass(object):
|
||||||
|
|
||||||
ba = design_data.get_bootaction('helloworld')
|
ba = design_data.get_bootaction('helloworld')
|
||||||
action_id = ulid2.generate_binary_ulid()
|
action_id = ulid2.generate_binary_ulid()
|
||||||
assets = ba.render_assets(target_host, design_data, action_id, design_ref)
|
assets = ba.render_assets(target_host, design_data, action_id,
|
||||||
|
design_ref)
|
||||||
|
|
||||||
assert len(assets) > 0
|
assert len(assets) > 0
|
||||||
|
|
||||||
|
|
|
@ -18,13 +18,15 @@ from drydock_provisioner.orchestrator.validations.validator import Validator
|
||||||
|
|
||||||
|
|
||||||
class TestDesignValidator(object):
|
class TestDesignValidator(object):
|
||||||
def test_validate_design(self, deckhand_ingester, drydock_state, input_files):
|
def test_validate_design(self, deckhand_ingester, drydock_state,
|
||||||
|
input_files):
|
||||||
"""Test the basic validation engine."""
|
"""Test the basic validation engine."""
|
||||||
|
|
||||||
input_file = input_files.join("deckhand_fullsite.yaml")
|
input_file = input_files.join("deckhand_fullsite.yaml")
|
||||||
design_ref = "file://%s" % str(input_file)
|
design_ref = "file://%s" % str(input_file)
|
||||||
|
|
||||||
orch = Orchestrator(state_manager=drydock_state, ingester=deckhand_ingester)
|
orch = Orchestrator(
|
||||||
|
state_manager=drydock_state, ingester=deckhand_ingester)
|
||||||
|
|
||||||
status, site_design = Orchestrator.get_effective_site(orch, design_ref)
|
status, site_design = Orchestrator.get_effective_site(orch, design_ref)
|
||||||
|
|
||||||
|
|
|
@ -20,12 +20,14 @@ from drydock_provisioner.orchestrator.validations.validator import Validator
|
||||||
|
|
||||||
|
|
||||||
class TestRationalBootStorage(object):
|
class TestRationalBootStorage(object):
|
||||||
def test_boot_storage_rational(self, deckhand_ingester, drydock_state, input_files):
|
def test_boot_storage_rational(self, deckhand_ingester, drydock_state,
|
||||||
|
input_files):
|
||||||
|
|
||||||
input_file = input_files.join("validation.yaml")
|
input_file = input_files.join("validation.yaml")
|
||||||
design_ref = "file://%s" % str(input_file)
|
design_ref = "file://%s" % str(input_file)
|
||||||
|
|
||||||
orch = Orchestrator(state_manager=drydock_state, ingester=deckhand_ingester)
|
orch = Orchestrator(
|
||||||
|
state_manager=drydock_state, ingester=deckhand_ingester)
|
||||||
|
|
||||||
status, site_design = Orchestrator.get_effective_site(orch, design_ref)
|
status, site_design = Orchestrator.get_effective_site(orch, design_ref)
|
||||||
|
|
||||||
|
@ -36,18 +38,21 @@ class TestRationalBootStorage(object):
|
||||||
assert msg.get('error') is False
|
assert msg.get('error') is False
|
||||||
assert len(message_list) == 1
|
assert len(message_list) == 1
|
||||||
|
|
||||||
def test_invalid_boot_storage_small(self, deckhand_ingester, drydock_state, input_files):
|
def test_invalid_boot_storage_small(self, deckhand_ingester, drydock_state,
|
||||||
|
input_files):
|
||||||
|
|
||||||
input_file = input_files.join("invalid_boot_storage_small.yaml")
|
input_file = input_files.join("invalid_boot_storage_small.yaml")
|
||||||
design_ref = "file://%s" % str(input_file)
|
design_ref = "file://%s" % str(input_file)
|
||||||
|
|
||||||
orch = Orchestrator(state_manager=drydock_state, ingester=deckhand_ingester)
|
orch = Orchestrator(
|
||||||
|
state_manager=drydock_state, ingester=deckhand_ingester)
|
||||||
|
|
||||||
status, site_design = Orchestrator.get_effective_site(orch, design_ref)
|
status, site_design = Orchestrator.get_effective_site(orch, design_ref)
|
||||||
|
|
||||||
message_list = Validator.boot_storage_rational(site_design)
|
message_list = Validator.boot_storage_rational(site_design)
|
||||||
|
|
||||||
regex = re.compile('Boot Storage Error: .+ volume must be > .+GB on BaremetalNode .+')
|
regex = re.compile(
|
||||||
|
'Boot Storage Error: .+ volume must be > .+GB on BaremetalNode .+')
|
||||||
|
|
||||||
for msg in message_list:
|
for msg in message_list:
|
||||||
msg = msg.to_dict()
|
msg = msg.to_dict()
|
||||||
|
@ -56,18 +61,22 @@ class TestRationalBootStorage(object):
|
||||||
|
|
||||||
assert len(message_list) == 4
|
assert len(message_list) == 4
|
||||||
|
|
||||||
def test_invalid_boot_storage_root_not_set(self, deckhand_ingester, drydock_state, input_files):
|
def test_invalid_boot_storage_root_not_set(self, deckhand_ingester,
|
||||||
|
drydock_state, input_files):
|
||||||
|
|
||||||
input_file = input_files.join("invalid_validation.yaml")
|
input_file = input_files.join("invalid_validation.yaml")
|
||||||
design_ref = "file://%s" % str(input_file)
|
design_ref = "file://%s" % str(input_file)
|
||||||
|
|
||||||
orch = Orchestrator(state_manager=drydock_state, ingester=deckhand_ingester)
|
orch = Orchestrator(
|
||||||
|
state_manager=drydock_state, ingester=deckhand_ingester)
|
||||||
|
|
||||||
status, site_design = Orchestrator.get_effective_site(orch, design_ref)
|
status, site_design = Orchestrator.get_effective_site(orch, design_ref)
|
||||||
|
|
||||||
message_list = Validator.boot_storage_rational(site_design)
|
message_list = Validator.boot_storage_rational(site_design)
|
||||||
|
|
||||||
regex = re.compile('Boot Storage Error: Root volume has to be set and must be > 20GB on BaremetalNode .+')
|
regex = re.compile(
|
||||||
|
'Boot Storage Error: Root volume has to be set and must be > 20GB on BaremetalNode .+'
|
||||||
|
)
|
||||||
|
|
||||||
for msg in message_list:
|
for msg in message_list:
|
||||||
msg = msg.to_dict()
|
msg = msg.to_dict()
|
||||||
|
|
|
@ -23,7 +23,8 @@ class TestIPLocality(object):
|
||||||
input_file = input_files.join("validation.yaml")
|
input_file = input_files.join("validation.yaml")
|
||||||
design_ref = "file://%s" % str(input_file)
|
design_ref = "file://%s" % str(input_file)
|
||||||
|
|
||||||
orch = Orchestrator(state_manager=drydock_state, ingester=deckhand_ingester)
|
orch = Orchestrator(
|
||||||
|
state_manager=drydock_state, ingester=deckhand_ingester)
|
||||||
|
|
||||||
status, site_design = Orchestrator.get_effective_site(orch, design_ref)
|
status, site_design = Orchestrator.get_effective_site(orch, design_ref)
|
||||||
|
|
||||||
|
@ -33,11 +34,13 @@ class TestIPLocality(object):
|
||||||
assert msg.get('message') == 'IP Locality Success'
|
assert msg.get('message') == 'IP Locality Success'
|
||||||
assert msg.get('error') is False
|
assert msg.get('error') is False
|
||||||
|
|
||||||
def test_ip_locality_no_networks(self, input_files, drydock_state, deckhand_ingester):
|
def test_ip_locality_no_networks(self, input_files, drydock_state,
|
||||||
|
deckhand_ingester):
|
||||||
input_file = input_files.join("ip_locality_no_networks.yaml")
|
input_file = input_files.join("ip_locality_no_networks.yaml")
|
||||||
design_ref = "file://%s" % str(input_file)
|
design_ref = "file://%s" % str(input_file)
|
||||||
|
|
||||||
orch = Orchestrator(state_manager=drydock_state, ingester=deckhand_ingester)
|
orch = Orchestrator(
|
||||||
|
state_manager=drydock_state, ingester=deckhand_ingester)
|
||||||
|
|
||||||
status, site_design = Orchestrator.get_effective_site(orch, design_ref)
|
status, site_design = Orchestrator.get_effective_site(orch, design_ref)
|
||||||
|
|
||||||
|
@ -47,11 +50,13 @@ class TestIPLocality(object):
|
||||||
assert msg.get('message') == 'No networks found.'
|
assert msg.get('message') == 'No networks found.'
|
||||||
assert msg.get('error') is False
|
assert msg.get('error') is False
|
||||||
|
|
||||||
def test_ip_locality_no_gateway(self, input_files, drydock_state, deckhand_ingester):
|
def test_ip_locality_no_gateway(self, input_files, drydock_state,
|
||||||
|
deckhand_ingester):
|
||||||
input_file = input_files.join("ip_locality_no_gateway.yaml")
|
input_file = input_files.join("ip_locality_no_gateway.yaml")
|
||||||
design_ref = "file://%s" % str(input_file)
|
design_ref = "file://%s" % str(input_file)
|
||||||
|
|
||||||
orch = Orchestrator(state_manager=drydock_state, ingester=deckhand_ingester)
|
orch = Orchestrator(
|
||||||
|
state_manager=drydock_state, ingester=deckhand_ingester)
|
||||||
|
|
||||||
status, site_design = Orchestrator.get_effective_site(orch, design_ref)
|
status, site_design = Orchestrator.get_effective_site(orch, design_ref)
|
||||||
|
|
||||||
|
@ -61,11 +66,13 @@ class TestIPLocality(object):
|
||||||
assert 'No gateway found' in msg.get('message')
|
assert 'No gateway found' in msg.get('message')
|
||||||
assert msg.get('error') is True
|
assert msg.get('error') is True
|
||||||
|
|
||||||
def test_no_baremetal_node(self, input_files, drydock_state, deckhand_ingester):
|
def test_no_baremetal_node(self, input_files, drydock_state,
|
||||||
|
deckhand_ingester):
|
||||||
input_file = input_files.join("no_baremetal_node.yaml")
|
input_file = input_files.join("no_baremetal_node.yaml")
|
||||||
design_ref = "file://%s" % str(input_file)
|
design_ref = "file://%s" % str(input_file)
|
||||||
|
|
||||||
orch = Orchestrator(state_manager=drydock_state, ingester=deckhand_ingester)
|
orch = Orchestrator(
|
||||||
|
state_manager=drydock_state, ingester=deckhand_ingester)
|
||||||
|
|
||||||
status, site_design = Orchestrator.get_effective_site(orch, design_ref)
|
status, site_design = Orchestrator.get_effective_site(orch, design_ref)
|
||||||
|
|
||||||
|
@ -75,24 +82,30 @@ class TestIPLocality(object):
|
||||||
assert msg.get('message') == 'No baremetal_nodes found.'
|
assert msg.get('message') == 'No baremetal_nodes found.'
|
||||||
assert msg.get('error') is False
|
assert msg.get('error') is False
|
||||||
|
|
||||||
def test_invalid_ip_locality_invalid_network(self, input_files, drydock_state, deckhand_ingester):
|
def test_invalid_ip_locality_invalid_network(
|
||||||
|
self, input_files, drydock_state, deckhand_ingester):
|
||||||
input_file = input_files.join("invalid_validation.yaml")
|
input_file = input_files.join("invalid_validation.yaml")
|
||||||
design_ref = "file://%s" % str(input_file)
|
design_ref = "file://%s" % str(input_file)
|
||||||
|
|
||||||
orch = Orchestrator(state_manager=drydock_state, ingester=deckhand_ingester)
|
orch = Orchestrator(
|
||||||
|
state_manager=drydock_state, ingester=deckhand_ingester)
|
||||||
|
|
||||||
status, site_design = Orchestrator.get_effective_site(orch, design_ref)
|
status, site_design = Orchestrator.get_effective_site(orch, design_ref)
|
||||||
|
|
||||||
message_list = Validator.ip_locality_check(site_design)
|
message_list = Validator.ip_locality_check(site_design)
|
||||||
|
|
||||||
regex = re.compile('IP Locality Error: The gateway IP Address .+ is not within the defined CIDR: .+ of .+')
|
regex = re.compile(
|
||||||
|
'IP Locality Error: The gateway IP Address .+ is not within the defined CIDR: .+ of .+'
|
||||||
|
)
|
||||||
regex_1 = re.compile('IP Locality Error: .+ is not a valid network.')
|
regex_1 = re.compile('IP Locality Error: .+ is not a valid network.')
|
||||||
regex_2 = re.compile('IP Locality Error: The IP Address .+ is not within the defined CIDR: .+ of .+ .')
|
regex_2 = re.compile(
|
||||||
|
'IP Locality Error: The IP Address .+ is not within the defined CIDR: .+ of .+ .'
|
||||||
|
)
|
||||||
|
|
||||||
assert len(message_list) == 3
|
assert len(message_list) == 3
|
||||||
for msg in message_list:
|
for msg in message_list:
|
||||||
msg = msg.to_dict()
|
msg = msg.to_dict()
|
||||||
assert msg.get('error')
|
assert msg.get('error')
|
||||||
assert (regex.match(msg.get('message')) is not None or
|
assert (regex.match(msg.get('message')) is not None
|
||||||
regex_1.match(msg.get('message')) is not None or
|
or regex_1.match(msg.get('message')) is not None
|
||||||
regex_2.match(msg.get('message')) is not None)
|
or regex_2.match(msg.get('message')) is not None)
|
||||||
|
|
|
@ -25,7 +25,8 @@ class TestMtu(object):
|
||||||
input_file = input_files.join("validation.yaml")
|
input_file = input_files.join("validation.yaml")
|
||||||
design_ref = "file://%s" % str(input_file)
|
design_ref = "file://%s" % str(input_file)
|
||||||
|
|
||||||
orch = Orchestrator(state_manager=drydock_state, ingester=deckhand_ingester)
|
orch = Orchestrator(
|
||||||
|
state_manager=drydock_state, ingester=deckhand_ingester)
|
||||||
|
|
||||||
status, site_design = Orchestrator.get_effective_site(orch, design_ref)
|
status, site_design = Orchestrator.get_effective_site(orch, design_ref)
|
||||||
|
|
||||||
|
@ -36,23 +37,30 @@ class TestMtu(object):
|
||||||
assert msg.get('error') is False
|
assert msg.get('error') is False
|
||||||
assert len(message_list) == 1
|
assert len(message_list) == 1
|
||||||
|
|
||||||
def test_invalid_mtu(self, mocker, deckhand_ingester, drydock_state, input_files):
|
def test_invalid_mtu(self, mocker, deckhand_ingester, drydock_state,
|
||||||
|
input_files):
|
||||||
|
|
||||||
input_file = input_files.join("invalid_validation.yaml")
|
input_file = input_files.join("invalid_validation.yaml")
|
||||||
design_ref = "file://%s" % str(input_file)
|
design_ref = "file://%s" % str(input_file)
|
||||||
|
|
||||||
orch = Orchestrator(state_manager=drydock_state, ingester=deckhand_ingester)
|
orch = Orchestrator(
|
||||||
|
state_manager=drydock_state, ingester=deckhand_ingester)
|
||||||
|
|
||||||
status, site_design = Orchestrator.get_effective_site(orch, design_ref)
|
status, site_design = Orchestrator.get_effective_site(orch, design_ref)
|
||||||
|
|
||||||
message_list = Validator.mtu_rational(site_design)
|
message_list = Validator.mtu_rational(site_design)
|
||||||
|
|
||||||
regex = re.compile('Mtu Error: Mtu must be between 1400 and 64000; on Network .+')
|
regex = re.compile(
|
||||||
regex_1 = re.compile('Mtu Error: Mtu must be <= the parent Network Link; for Network .+')
|
'Mtu Error: Mtu must be between 1400 and 64000; on Network .+')
|
||||||
|
regex_1 = re.compile(
|
||||||
|
'Mtu Error: Mtu must be <= the parent Network Link; for Network .+'
|
||||||
|
)
|
||||||
|
|
||||||
for msg in message_list:
|
for msg in message_list:
|
||||||
msg = msg.to_dict()
|
msg = msg.to_dict()
|
||||||
assert msg.get('error')
|
assert msg.get('error')
|
||||||
assert regex.match(msg.get('message')) is not None or regex_1.match(msg.get('message')) is not None
|
assert regex.match(
|
||||||
|
msg.get('message')) is not None or regex_1.match(
|
||||||
|
msg.get('message')) is not None
|
||||||
|
|
||||||
assert len(message_list) == 4
|
assert len(message_list) == 4
|
||||||
|
|
|
@ -50,12 +50,18 @@ class TestRationalNetworkLinkBond(object):
|
||||||
|
|
||||||
message_list = Validator.rational_network_bond(site_design)
|
message_list = Validator.rational_network_bond(site_design)
|
||||||
|
|
||||||
regex = re.compile('Network Link Bonding Error: Down delay is less than mon rate on BaremetalNode .+')
|
regex = re.compile(
|
||||||
regex_1 = re.compile('Network Link Bonding Error: Up delay is less than mon rate on BaremetalNode .+')
|
'Network Link Bonding Error: Down delay is less than mon rate on BaremetalNode .+'
|
||||||
|
)
|
||||||
|
regex_1 = re.compile(
|
||||||
|
'Network Link Bonding Error: Up delay is less than mon rate on BaremetalNode .+'
|
||||||
|
)
|
||||||
|
|
||||||
for msg in message_list:
|
for msg in message_list:
|
||||||
msg = msg.to_dict()
|
msg = msg.to_dict()
|
||||||
assert msg.get('error') is True
|
assert msg.get('error') is True
|
||||||
assert regex.match(msg.get('message')) is not None or regex_1.match(msg.get('message')) is not None
|
assert regex.match(
|
||||||
|
msg.get('message')) is not None or regex_1.match(
|
||||||
|
msg.get('message')) is not None
|
||||||
|
|
||||||
assert len(message_list) == 2
|
assert len(message_list) == 2
|
||||||
|
|
|
@ -48,15 +48,19 @@ class TestRationalNetworkTrunking(object):
|
||||||
|
|
||||||
message_list = Validator.network_trunking_rational(site_design)
|
message_list = Validator.network_trunking_rational(site_design)
|
||||||
|
|
||||||
regex = re.compile('Rational Network Trunking Error: Trunking mode is disabled, a trunking'
|
regex = re.compile(
|
||||||
'default_network must be defined; on NetworkLink .+')
|
'Rational Network Trunking Error: Trunking mode is disabled, a trunking'
|
||||||
|
'default_network must be defined; on NetworkLink .+')
|
||||||
|
|
||||||
regex_1 = re.compile('Rational Network Trunking Error: If there is more than 1 allowed network,'
|
regex_1 = re.compile(
|
||||||
'trunking mode must be enabled; on NetworkLink .+')
|
'Rational Network Trunking Error: If there is more than 1 allowed network,'
|
||||||
|
'trunking mode must be enabled; on NetworkLink .+')
|
||||||
|
|
||||||
for msg in message_list:
|
for msg in message_list:
|
||||||
msg = msg.to_dict()
|
msg = msg.to_dict()
|
||||||
assert msg.get('error')
|
assert msg.get('error')
|
||||||
assert regex.match(msg.get('message')) is not None or regex_1.match(msg.get('message')) is not None
|
assert regex.match(
|
||||||
|
msg.get('message')) is not None or regex_1.match(
|
||||||
|
msg.get('message')) is not None
|
||||||
|
|
||||||
assert len(message_list) == 2
|
assert len(message_list) == 2
|
||||||
|
|
|
@ -24,8 +24,8 @@ class TestDuplicateIPs(object):
|
||||||
input_file = input_files.join("validation.yaml")
|
input_file = input_files.join("validation.yaml")
|
||||||
design_ref = "file://%s" % str(input_file)
|
design_ref = "file://%s" % str(input_file)
|
||||||
|
|
||||||
orch = Orchestrator(state_manager=drydock_state,
|
orch = Orchestrator(
|
||||||
ingester=deckhand_ingester)
|
state_manager=drydock_state, ingester=deckhand_ingester)
|
||||||
|
|
||||||
status, site_design = Orchestrator.get_effective_site(orch, design_ref)
|
status, site_design = Orchestrator.get_effective_site(orch, design_ref)
|
||||||
|
|
||||||
|
@ -37,12 +37,11 @@ class TestDuplicateIPs(object):
|
||||||
|
|
||||||
def test_no_duplicate_IPs_no_baremetal_node(
|
def test_no_duplicate_IPs_no_baremetal_node(
|
||||||
self, input_files, drydock_state, deckhand_ingester):
|
self, input_files, drydock_state, deckhand_ingester):
|
||||||
input_file = input_files.join(
|
input_file = input_files.join("no_baremetal_node.yaml")
|
||||||
"no_baremetal_node.yaml")
|
|
||||||
design_ref = "file://%s" % str(input_file)
|
design_ref = "file://%s" % str(input_file)
|
||||||
|
|
||||||
orch = Orchestrator(state_manager=drydock_state,
|
orch = Orchestrator(
|
||||||
ingester=deckhand_ingester)
|
state_manager=drydock_state, ingester=deckhand_ingester)
|
||||||
|
|
||||||
status, site_design = Orchestrator.get_effective_site(orch, design_ref)
|
status, site_design = Orchestrator.get_effective_site(orch, design_ref)
|
||||||
|
|
||||||
|
@ -52,13 +51,13 @@ class TestDuplicateIPs(object):
|
||||||
assert msg.get('message') == 'No BaremetalNodes Found.'
|
assert msg.get('message') == 'No BaremetalNodes Found.'
|
||||||
assert msg.get('error') is False
|
assert msg.get('error') is False
|
||||||
|
|
||||||
def test_no_duplicate_IPs_no_addressing(
|
def test_no_duplicate_IPs_no_addressing(self, input_files, drydock_state,
|
||||||
self, input_files, drydock_state, deckhand_ingester):
|
deckhand_ingester):
|
||||||
input_file = input_files.join("no_duplicate_IPs_no_addressing.yaml")
|
input_file = input_files.join("no_duplicate_IPs_no_addressing.yaml")
|
||||||
design_ref = "file://%s" % str(input_file)
|
design_ref = "file://%s" % str(input_file)
|
||||||
|
|
||||||
orch = Orchestrator(state_manager=drydock_state,
|
orch = Orchestrator(
|
||||||
ingester=deckhand_ingester)
|
state_manager=drydock_state, ingester=deckhand_ingester)
|
||||||
|
|
||||||
status, site_design = Orchestrator.get_effective_site(orch, design_ref)
|
status, site_design = Orchestrator.get_effective_site(orch, design_ref)
|
||||||
|
|
||||||
|
@ -68,19 +67,21 @@ class TestDuplicateIPs(object):
|
||||||
assert msg.get('message') == 'No BaremetalNodes Found.'
|
assert msg.get('message') == 'No BaremetalNodes Found.'
|
||||||
assert msg.get('error') is False
|
assert msg.get('error') is False
|
||||||
|
|
||||||
def test_invalid_no_duplicate_IPs(
|
def test_invalid_no_duplicate_IPs(self, input_files, drydock_state,
|
||||||
self, input_files, drydock_state, deckhand_ingester):
|
deckhand_ingester):
|
||||||
input_file = input_files.join("invalid_validation.yaml")
|
input_file = input_files.join("invalid_validation.yaml")
|
||||||
design_ref = "file://%s" % str(input_file)
|
design_ref = "file://%s" % str(input_file)
|
||||||
|
|
||||||
orch = Orchestrator(state_manager=drydock_state,
|
orch = Orchestrator(
|
||||||
ingester=deckhand_ingester)
|
state_manager=drydock_state, ingester=deckhand_ingester)
|
||||||
|
|
||||||
status, site_design = Orchestrator.get_effective_site(orch, design_ref)
|
status, site_design = Orchestrator.get_effective_site(orch, design_ref)
|
||||||
|
|
||||||
message_list = Validator.no_duplicate_IPs_check(site_design)
|
message_list = Validator.no_duplicate_IPs_check(site_design)
|
||||||
|
|
||||||
regex = re.compile('Error! Duplicate IP Address Found: .+ is in use by both .+ and .+.')
|
regex = re.compile(
|
||||||
|
'Error! Duplicate IP Address Found: .+ is in use by both .+ and .+.'
|
||||||
|
)
|
||||||
for msg in message_list:
|
for msg in message_list:
|
||||||
msg = msg.to_dict()
|
msg = msg.to_dict()
|
||||||
assert msg.get('error') is True
|
assert msg.get('error') is True
|
||||||
|
|
|
@ -37,11 +37,14 @@ class TestRationalNetworkTrunking(object):
|
||||||
assert msg.get('message') == 'Storage Partitioning'
|
assert msg.get('message') == 'Storage Partitioning'
|
||||||
assert msg.get('error') is False
|
assert msg.get('error') is False
|
||||||
|
|
||||||
def test_storage_partitioning_unassigned_partition(self, deckhand_ingester, drydock_state, input_files):
|
def test_storage_partitioning_unassigned_partition(
|
||||||
input_file = input_files.join("storage_partitioning_unassigned_partition.yaml")
|
self, deckhand_ingester, drydock_state, input_files):
|
||||||
|
input_file = input_files.join(
|
||||||
|
"storage_partitioning_unassigned_partition.yaml")
|
||||||
design_ref = "file://%s" % str(input_file)
|
design_ref = "file://%s" % str(input_file)
|
||||||
|
|
||||||
orch = Orchestrator(state_manager=drydock_state, ingester=deckhand_ingester)
|
orch = Orchestrator(
|
||||||
|
state_manager=drydock_state, ingester=deckhand_ingester)
|
||||||
|
|
||||||
status, site_design = Orchestrator.get_effective_site(orch, design_ref)
|
status, site_design = Orchestrator.get_effective_site(orch, design_ref)
|
||||||
|
|
||||||
|
@ -65,8 +68,9 @@ class TestRationalNetworkTrunking(object):
|
||||||
|
|
||||||
message_list = Validator.storage_partitioning(site_design)
|
message_list = Validator.storage_partitioning(site_design)
|
||||||
|
|
||||||
regex = re.compile('Storage Partitioning Error: A volume group must be assigned to a storage device or '
|
regex = re.compile(
|
||||||
'partition; volume group .+ on BaremetalNode .+')
|
'Storage Partitioning Error: A volume group must be assigned to a storage device or '
|
||||||
|
'partition; volume group .+ on BaremetalNode .+')
|
||||||
|
|
||||||
for msg in message_list:
|
for msg in message_list:
|
||||||
msg = msg.to_dict()
|
msg = msg.to_dict()
|
||||||
|
|
|
@ -20,12 +20,14 @@ from drydock_provisioner.orchestrator.validations.validator import Validator
|
||||||
|
|
||||||
|
|
||||||
class TestStorageSizing(object):
|
class TestStorageSizing(object):
|
||||||
def test_storage_sizing(self, deckhand_ingester, drydock_state, input_files):
|
def test_storage_sizing(self, deckhand_ingester, drydock_state,
|
||||||
|
input_files):
|
||||||
|
|
||||||
input_file = input_files.join("storage_sizing.yaml")
|
input_file = input_files.join("storage_sizing.yaml")
|
||||||
design_ref = "file://%s" % str(input_file)
|
design_ref = "file://%s" % str(input_file)
|
||||||
|
|
||||||
orch = Orchestrator(state_manager=drydock_state, ingester=deckhand_ingester)
|
orch = Orchestrator(
|
||||||
|
state_manager=drydock_state, ingester=deckhand_ingester)
|
||||||
|
|
||||||
status, site_design = Orchestrator.get_effective_site(orch, design_ref)
|
status, site_design = Orchestrator.get_effective_site(orch, design_ref)
|
||||||
|
|
||||||
|
@ -36,22 +38,30 @@ class TestStorageSizing(object):
|
||||||
assert msg.get('message') == 'Storage Sizing'
|
assert msg.get('message') == 'Storage Sizing'
|
||||||
assert msg.get('error') is False
|
assert msg.get('error') is False
|
||||||
|
|
||||||
def test_invalid_storage_sizing(self, deckhand_ingester, drydock_state, input_files):
|
def test_invalid_storage_sizing(self, deckhand_ingester, drydock_state,
|
||||||
|
input_files):
|
||||||
|
|
||||||
input_file = input_files.join("invalid_validation.yaml")
|
input_file = input_files.join("invalid_validation.yaml")
|
||||||
design_ref = "file://%s" % str(input_file)
|
design_ref = "file://%s" % str(input_file)
|
||||||
|
|
||||||
orch = Orchestrator(state_manager=drydock_state, ingester=deckhand_ingester)
|
orch = Orchestrator(
|
||||||
|
state_manager=drydock_state, ingester=deckhand_ingester)
|
||||||
|
|
||||||
status, site_design = Orchestrator.get_effective_site(orch, design_ref)
|
status, site_design = Orchestrator.get_effective_site(orch, design_ref)
|
||||||
|
|
||||||
message_list = Validator.storage_sizing(site_design)
|
message_list = Validator.storage_sizing(site_design)
|
||||||
|
|
||||||
regex = re.compile('Storage Sizing Error: Storage .+ size is < 0 on Baremetal Node .+')
|
regex = re.compile(
|
||||||
regex_1 = re.compile('Storage Sizing Error: Storage .+ size is greater than 99 on Baremetal Node .+')
|
'Storage Sizing Error: Storage .+ size is < 0 on Baremetal Node .+'
|
||||||
|
)
|
||||||
|
regex_1 = re.compile(
|
||||||
|
'Storage Sizing Error: Storage .+ size is greater than 99 on Baremetal Node .+'
|
||||||
|
)
|
||||||
|
|
||||||
assert len(message_list) == 6
|
assert len(message_list) == 6
|
||||||
for msg in message_list:
|
for msg in message_list:
|
||||||
msg = msg.to_dict()
|
msg = msg.to_dict()
|
||||||
assert regex.match(msg.get('message')) is not None or regex_1.match(msg.get('message')) is not None
|
assert regex.match(
|
||||||
|
msg.get('message')) is not None or regex_1.match(
|
||||||
|
msg.get('message')) is not None
|
||||||
assert msg.get('error') is True
|
assert msg.get('error') is True
|
||||||
|
|
|
@ -20,7 +20,8 @@ from drydock_provisioner.orchestrator.validations.validator import Validator
|
||||||
|
|
||||||
|
|
||||||
class TestUniqueNetwork(object):
|
class TestUniqueNetwork(object):
|
||||||
def test_unique_network(self, mocker, deckhand_ingester, drydock_state, input_files):
|
def test_unique_network(self, mocker, deckhand_ingester, drydock_state,
|
||||||
|
input_files):
|
||||||
|
|
||||||
input_file = input_files.join("validation.yaml")
|
input_file = input_files.join("validation.yaml")
|
||||||
design_ref = "file://%s" % str(input_file)
|
design_ref = "file://%s" % str(input_file)
|
||||||
|
@ -37,7 +38,8 @@ class TestUniqueNetwork(object):
|
||||||
assert msg.get('error') is False
|
assert msg.get('error') is False
|
||||||
assert len(message_list) == 1
|
assert len(message_list) == 1
|
||||||
|
|
||||||
def test_invalid_unique_network(self, mocker, deckhand_ingester, drydock_state, input_files):
|
def test_invalid_unique_network(self, mocker, deckhand_ingester,
|
||||||
|
drydock_state, input_files):
|
||||||
|
|
||||||
input_file = input_files.join("invalid_unique_network.yaml")
|
input_file = input_files.join("invalid_unique_network.yaml")
|
||||||
design_ref = "file://%s" % str(input_file)
|
design_ref = "file://%s" % str(input_file)
|
||||||
|
@ -49,7 +51,9 @@ class TestUniqueNetwork(object):
|
||||||
|
|
||||||
message_list = Validator.unique_network_check(site_design)
|
message_list = Validator.unique_network_check(site_design)
|
||||||
|
|
||||||
regex = re.compile('Unique Network Error: Allowed network .+ duplicated on NetworkLink .+ and NetworkLink .+')
|
regex = re.compile(
|
||||||
|
'Unique Network Error: Allowed network .+ duplicated on NetworkLink .+ and NetworkLink .+'
|
||||||
|
)
|
||||||
|
|
||||||
for msg in message_list:
|
for msg in message_list:
|
||||||
msg = msg.to_dict()
|
msg = msg.to_dict()
|
||||||
|
|
Loading…
Reference in New Issue