From 4a1367a39ad17d93d9d4469b8b2f7fcd73fa69ce Mon Sep 17 00:00:00 2001 From: Scott Hussey Date: Thu, 18 Jan 2018 12:49:09 -0600 Subject: [PATCH] Support image and kernel selection - Image selection based of Ubuntu series name (e.g. 'xenial') - Kernel selection based on MAAS kernel names of 'ga-16.04' or 'hwe-16.04' - Validation that defined images and kernels are valid in the current node driver - Unit test for the validation Change-Id: I6256a2e00a4594af38a2d862c738e03efb7ddb29 --- .gitignore | 3 + drydock_provisioner/drivers/node/driver.py | 14 + .../drivers/node/maasdriver/actions/node.py | 5 +- .../drivers/node/maasdriver/api_client.py | 1 + .../drivers/node/maasdriver/driver.py | 26 ++ .../node/maasdriver/models/boot_resource.py | 45 +++ .../drivers/node/maasdriver/models/machine.py | 3 +- .../orchestrator/actions/orchestrator.py | 3 +- .../orchestrator/orchestrator.py | 2 +- .../orchestrator/validations/validator.py | 111 +++++- tests/conftest.py | 5 + .../postgres/test_action_prepare_nodes.py | 9 +- tests/unit/test_validate_design.py | 2 +- .../test_validation_rule_valid_platform.py | 85 +++++ tests/yaml_samples/deckhand_fullsite.yaml | 4 +- tests/yaml_samples/invalid_kernel.yaml | 329 ++++++++++++++++++ tests/yaml_samples/validation.yaml | 4 +- 17 files changed, 620 insertions(+), 31 deletions(-) create mode 100644 tests/unit/test_validation_rule_valid_platform.py create mode 100644 tests/yaml_samples/invalid_kernel.yaml diff --git a/.gitignore b/.gitignore index 8ecf9b4c..5a97a3e3 100644 --- a/.gitignore +++ b/.gitignore @@ -101,3 +101,6 @@ ENV/ # IDEA IDE .idea/ + +# VIM +.*.swp diff --git a/drydock_provisioner/drivers/node/driver.py b/drydock_provisioner/drivers/node/driver.py index 06d6737b..fb00fd52 100644 --- a/drydock_provisioner/drivers/node/driver.py +++ b/drydock_provisioner/drivers/node/driver.py @@ -57,3 +57,17 @@ class NodeDriver(ProviderDriver): else: raise errors.DriverError("Unsupported action %s for driver %s" % (task_action, self.driver_desc)) + + def get_available_images(self): + """Return images that can be deployed to nodes by this driver.""" + + return [] + + def get_available_kernels(self, image): + """Return a list of kernels that can be specified for deployment. + + :param image: str specifying what image the kernel will be activated + within + """ + + return [] diff --git a/drydock_provisioner/drivers/node/maasdriver/actions/node.py b/drydock_provisioner/drivers/node/maasdriver/actions/node.py index da35b7ac..f172bd85 100644 --- a/drydock_provisioner/drivers/node/maasdriver/actions/node.py +++ b/drydock_provisioner/drivers/node/maasdriver/actions/node.py @@ -1801,10 +1801,11 @@ class DeployNode(BaseMaasAction): "Error setting boot action id key tag for %s." % n.name, exc_info=ex) - self.logger.info("Deploying node %s" % (n.name)) + self.logger.info("Deploying node %s: image=%s, kernel=%s" % + (n.name, n.image, n.kernel)) try: - machine.deploy() + machine.deploy(platform=n.image, kernel=n.kernel) except errors.DriverError: msg = "Error deploying node %s, skipping" % n.name self.logger.warning(msg) diff --git a/drydock_provisioner/drivers/node/maasdriver/api_client.py b/drydock_provisioner/drivers/node/maasdriver/api_client.py index f4513673..ed8c5a69 100644 --- a/drydock_provisioner/drivers/node/maasdriver/api_client.py +++ b/drydock_provisioner/drivers/node/maasdriver/api_client.py @@ -176,6 +176,7 @@ class MaasRequestFactory(object): self.logger.debug( "Received error response - URL: %s %s - RESPONSE: %s" % (prepared_req.method, prepared_req.url, resp.status_code)) + self.logger.debug("Response content: %s" % resp.text) raise errors.DriverError("MAAS Error: %s - %s" % (resp.status_code, resp.text)) return resp diff --git a/drydock_provisioner/drivers/node/maasdriver/driver.py b/drydock_provisioner/drivers/node/maasdriver/driver.py index 1f5713be..dcfb0503 100644 --- a/drydock_provisioner/drivers/node/maasdriver/driver.py +++ b/drydock_provisioner/drivers/node/maasdriver/driver.py @@ -24,6 +24,7 @@ import drydock_provisioner.config as config from drydock_provisioner.drivers.node.driver import NodeDriver from drydock_provisioner.drivers.node.maasdriver.api_client import MaasRequestFactory +from drydock_provisioner.drivers.node.maasdriver.models.boot_resource import BootResources from .actions.node import ValidateNodeServices from .actions.node import CreateStorageTemplate @@ -207,6 +208,31 @@ class MaasNodeDriver(NodeDriver): return + def get_available_images(self): + """Return images available in MAAS.""" + maas_client = MaasRequestFactory( + config.config_mgr.conf.maasdriver.maas_api_url, + config.config_mgr.conf.maasdriver.maas_api_key) + + br = BootResources(maas_client) + br.refresh() + + return br.get_available_images() + + def get_available_kernels(self, image_name): + """Return kernels available for ``image_name``. + + :param image_name: str image name (e.g. 'xenial') + """ + maas_client = MaasRequestFactory( + config.config_mgr.conf.maasdriver.maas_api_url, + config.config_mgr.conf.maasdriver.maas_api_key) + + br = BootResources(maas_client) + br.refresh() + + return br.get_available_kernels(image_name) + def list_opts(): return {MaasNodeDriver.driver_key: MaasNodeDriver.maasdriver_options} diff --git a/drydock_provisioner/drivers/node/maasdriver/models/boot_resource.py b/drydock_provisioner/drivers/node/maasdriver/models/boot_resource.py index 6539fa5d..6601c5eb 100644 --- a/drydock_provisioner/drivers/node/maasdriver/models/boot_resource.py +++ b/drydock_provisioner/drivers/node/maasdriver/models/boot_resource.py @@ -37,6 +37,28 @@ class BootResource(model_base.ResourceBase): def __init__(self, api_client, **kwargs): super().__init__(api_client, **kwargs) + def get_image_name(self): + """Return the name that would be specified in a deployment. + + Return None if this is not an ubuntu image, otherwise + the distro series name + """ + (os, release) = self.name.split('/') + + # Only supply image names for ubuntu-based images + if os == 'ubuntu': + return release + else: + # Non-ubuntu images such as the uefi bootloader + # should never be selectable + return None + + def get_kernel_name(self): + """Return the kernel name that would be specified in a deployment.""" + (_, kernel) = self.architecture.split('/') + + return kernel + class BootResources(model_base.ResourceCollectionBase): @@ -62,3 +84,26 @@ class BootResources(model_base.ResourceCollectionBase): resp.status_code, resp.text) self.logger.error(msg) raise errors.DriverError(msg) + + def get_available_images(self): + """Get list of available deployable images.""" + image_options = list() + for k, v in self.resources.items(): + if v.get_image_name() not in image_options: + image_options.append(v.get_image_name()) + return image_options + + def get_available_kernels(self, image_name): + """Get kernels available for image_name + + Return list of kernel names available for + ``image_name``. + + :param image_name: str image_name (e.g. 'xenial') + """ + kernel_options = list() + for k, v in self.resources.items(): + if (v.get_image_name() == image_name + and v.get_kernel_name() not in kernel_options): + kernel_options.append(v.get_kernel_name()) + return kernel_options diff --git a/drydock_provisioner/drivers/node/maasdriver/models/machine.py b/drydock_provisioner/drivers/node/maasdriver/models/machine.py index 67c63b48..f71e3087 100644 --- a/drydock_provisioner/drivers/node/maasdriver/models/machine.py +++ b/drydock_provisioner/drivers/node/maasdriver/models/machine.py @@ -457,8 +457,7 @@ class Machines(model_base.ResourceCollectionBase): if k.startswith('power_params.'): field = k[13:] result = [ - i for i in result - if str( + i for i in result if str( getattr(i, 'power_parameters', {}).get(field, None)) == str(v) ] diff --git a/drydock_provisioner/orchestrator/actions/orchestrator.py b/drydock_provisioner/orchestrator/actions/orchestrator.py index ba0d12fa..e20b3f42 100644 --- a/drydock_provisioner/orchestrator/actions/orchestrator.py +++ b/drydock_provisioner/orchestrator/actions/orchestrator.py @@ -939,8 +939,7 @@ class BootactionReport(BaseAction): bas = self.state_manager.get_boot_actions_for_node(n) running_bas = { k: v - for (k, v) in bas.items() - if v.get('action_status') == + for (k, v) in bas.items() if v.get('action_status') == hd_fields.ActionResult.Incomplete } if len(running_bas) > 0: diff --git a/drydock_provisioner/orchestrator/orchestrator.py b/drydock_provisioner/orchestrator/orchestrator.py index 564288e6..d2f88758 100644 --- a/drydock_provisioner/orchestrator/orchestrator.py +++ b/drydock_provisioner/orchestrator/orchestrator.py @@ -281,7 +281,7 @@ class Orchestrator(object): """ status = None site_design = None - val = Validator() + val = Validator(self) try: status, site_design = self.get_described_site(design_ref) if status.status == hd_fields.ActionResult.Success: diff --git a/drydock_provisioner/orchestrator/validations/validator.py b/drydock_provisioner/orchestrator/validations/validator.py index 81998e65..0581bbc2 100644 --- a/drydock_provisioner/orchestrator/validations/validator.py +++ b/drydock_provisioner/orchestrator/validations/validator.py @@ -22,6 +22,13 @@ from drydock_provisioner.objects.task import TaskStatus, TaskStatusMessage class Validator(): + def __init__(self, orchestrator): + """Create a validator with a reference to the orchestrator. + + :param orchestrator: instance of Orchestrator + """ + self.orchestrator = orchestrator + def validate_design(self, site_design, result_status=None): """Validate the design in site_design passes all validation rules. @@ -32,13 +39,12 @@ class Validator(): :param site_design: instance of objects.SiteDesign :param result_status: instance of objects.TaskStatus """ - if result_status is None: result_status = TaskStatus() validation_error = False for rule in rule_set: - output = rule(site_design) + output = rule(site_design, orchestrator=self.orchestrator) result_status.message_list.extend(output) error_msg = [m for m in output if m.error] result_status.error_count = result_status.error_count + len( @@ -54,7 +60,66 @@ class Validator(): return result_status @classmethod - def rational_network_bond(cls, site_design): + def valid_platform_selection(cls, site_design, orchestrator=None): + """Validate that the platform selection for all nodes is valid. + + Each node specifies an ``image`` and a ``kernel`` to use for + deployment. Check that these are valid for the image repoistory + configured in MAAS. + """ + message_list = list() + + try: + node_driver = orchestrator.enabled_drivers['node'] + except KeyError: + message_list.append( + TaskStatusMessage( + msg="Platform Validation: No enabled node driver, image" + "and kernel selections not validated.", + error=False, + ctx_type='NA', + ctx='NA')) + return message_list + + valid_images = node_driver.get_available_images() + + valid_kernels = dict() + + for i in valid_images: + valid_kernels[i] = node_driver.get_available_kernels(i) + + for n in site_design.baremetal_nodes: + if n.image in valid_images: + if n.kernel in valid_kernels[n.image]: + continue + message_list.append( + TaskStatusMessage( + msg="Platform Validation: invalid kernel %s for node %s." + % (n.kernel, n.name), + error=True, + ctx_type='NA', + ctx='NA')) + continue + message_list.append( + TaskStatusMessage( + msg="Platform Validation: invalid image %s for node %s." % + (n.image, n.name), + error=True, + ctx_type='NA', + ctx='NA')) + if not message_list: + message_list.append( + TaskStatusMessage( + msg="Platform Validation: all nodes have valid " + "image and kernel selections.", + error=False, + ctx_type='NA', + ctx='NA')) + + return message_list + + @classmethod + def rational_network_bond(cls, site_design, orchestrator=None): """ 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. @@ -74,8 +139,7 @@ class Validator(): if bonding_mode == 'disabled': # check to make sure nothing else is specified if any([ - network_link.get(x) - for x in [ + network_link.get(x) for x in [ 'bonding_peer_rate', 'bonding_xmit_hash', 'bonding_mon_rate', 'bonding_up_delay', 'bonding_down_delay' @@ -144,7 +208,7 @@ class Validator(): return message_list @classmethod - def network_trunking_rational(cls, site_design): + def network_trunking_rational(cls, site_design, orchestrator=None): """ 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. @@ -194,7 +258,7 @@ class Validator(): return message_list @classmethod - def storage_partitioning(cls, site_design): + def storage_partitioning(cls, site_design, orchestrator=None): """ 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. @@ -276,7 +340,7 @@ class Validator(): return message_list @classmethod - def unique_network_check(cls, site_design): + def unique_network_check(cls, site_design, orchestrator=None): """ Ensures that each network name appears at most once between all NetworkLink allowed networks @@ -327,7 +391,7 @@ class Validator(): return message_list @classmethod - def mtu_rational(cls, site_design): + def mtu_rational(cls, site_design, orchestrator=None): """ Ensure that the MTU for each network is equal or less than the MTU defined for the parent NetworkLink for that network. @@ -385,7 +449,7 @@ class Validator(): return message_list @classmethod - def storage_sizing(cls, site_design): + def storage_sizing(cls, site_design, orchestrator=None): """ Ensures that for a partitioned physical device or logical volumes in a volume group, if sizing is a percentage then those percentages @@ -469,7 +533,7 @@ class Validator(): return message_list @classmethod - def no_duplicate_IPs_check(cls, site_design): + def no_duplicate_IPs_check(cls, site_design, orchestrator=None): """ 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 @@ -483,7 +547,9 @@ 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', []) @@ -504,12 +570,14 @@ class Validator(): 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 @classmethod - def boot_storage_rational(cls, site_design): + def boot_storage_rational(cls, site_design, orchestrator=None): """ Ensures that root volume is defined and is at least 20GB and that boot volume is at least 1 GB """ @@ -599,7 +667,7 @@ class Validator(): return message_list @classmethod - def ip_locality_check(cls, site_design): + def ip_locality_check(cls, site_design, orchestrator=None): """ 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. @@ -613,7 +681,9 @@ 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') @@ -650,7 +720,9 @@ class Validator(): 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', []) @@ -687,7 +759,9 @@ class Validator(): 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 @@ -701,4 +775,5 @@ rule_set = [ Validator.ip_locality_check, Validator.no_duplicate_IPs_check, Validator.boot_storage_rational, + Validator.valid_platform_selection, ] diff --git a/tests/conftest.py b/tests/conftest.py index a0637fef..e764533a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -90,6 +90,11 @@ def setup(setup_logging): config.config_mgr.register_options(enable_keystone=False) config.config_mgr.conf([]) + config.config_mgr.conf.set_override( + name="node_driver", + group="plugins", + override= + "drydock_provisioner.drivers.node.maasdriver.driver.MaasNodeDriver") config.config_mgr.conf.set_override( name="database_connect_string", group="database", diff --git a/tests/integration/postgres/test_action_prepare_nodes.py b/tests/integration/postgres/test_action_prepare_nodes.py index de93db60..ed1f4a61 100644 --- a/tests/integration/postgres/test_action_prepare_nodes.py +++ b/tests/integration/postgres/test_action_prepare_nodes.py @@ -19,8 +19,15 @@ from drydock_provisioner.orchestrator.actions.orchestrator import PrepareNodes class TestActionPrepareNodes(object): - def test_preparenodes(self, input_files, deckhand_ingester, setup, + def test_preparenodes(self, mocker, input_files, deckhand_ingester, setup, drydock_state): + mock_images = mocker.patch("drydock_provisioner.drivers.node.driver.NodeDriver" + ".get_available_images") + mock_images.return_value = ['xenial'] + mock_kernels = mocker.patch("drydock_provisioner.drivers.node.driver.NodeDriver" + ".get_available_kernels") + mock_kernels.return_value = ['ga-16.04', 'hwe-16.04'] + input_file = input_files.join("deckhand_fullsite.yaml") design_ref = "file://%s" % str(input_file) diff --git a/tests/unit/test_validate_design.py b/tests/unit/test_validate_design.py index b0e7226c..3a71e813 100644 --- a/tests/unit/test_validate_design.py +++ b/tests/unit/test_validate_design.py @@ -30,7 +30,7 @@ class TestDesignValidator(object): status, site_design = Orchestrator.get_effective_site(orch, design_ref) - val = Validator() + val = Validator(orch) response = val.validate_design(site_design) for msg in response.message_list: diff --git a/tests/unit/test_validation_rule_valid_platform.py b/tests/unit/test_validation_rule_valid_platform.py new file mode 100644 index 00000000..569188f4 --- /dev/null +++ b/tests/unit/test_validation_rule_valid_platform.py @@ -0,0 +1,85 @@ +# 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 Validation Rule Rational Boot Storage""" + +import drydock_provisioner.config as config + +from drydock_provisioner.orchestrator.orchestrator import Orchestrator +from drydock_provisioner.orchestrator.validations.validator import Validator + + +class TestValidPlatform(object): + def test_valid_platform(self, mocker, deckhand_ingester, drydock_state, + input_files): + mock_images = mocker.patch( + "drydock_provisioner.drivers.node.maasdriver.driver." + "MaasNodeDriver.get_available_images") + mock_images.return_value = ['xenial'] + mock_kernels = mocker.patch( + "drydock_provisioner.drivers.node.maasdriver.driver." + "MaasNodeDriver.get_available_kernels") + mock_kernels.return_value = ['ga-16.04', 'hwe-16.04'] + + input_file = input_files.join("validation.yaml") + design_ref = "file://%s" % str(input_file) + + orch = Orchestrator( + state_manager=drydock_state, + ingester=deckhand_ingester, + enabled_drivers=config.config_mgr.conf.plugins) + + status, site_design = Orchestrator.get_effective_site(orch, design_ref) + + message_list = Validator.valid_platform_selection( + site_design, orchestrator=orch) + for m in message_list: + print(m.to_dict()) + + msg = message_list[0].to_dict() + + assert 'all nodes have valid' in msg.get('message') + assert msg.get('error') is False + assert len(message_list) == 1 + + def test_invalid_platform(self, mocker, deckhand_ingester, drydock_state, + input_files): + mock_images = mocker.patch( + "drydock_provisioner.drivers.node.maasdriver.driver." + "MaasNodeDriver.get_available_images") + mock_images.return_value = ['xenial'] + mock_kernels = mocker.patch( + "drydock_provisioner.drivers.node.maasdriver.driver." + "MaasNodeDriver.get_available_kernels") + mock_kernels.return_value = ['ga-16.04', 'hwe-16.04'] + + input_file = input_files.join("invalid_kernel.yaml") + design_ref = "file://%s" % str(input_file) + + orch = Orchestrator( + state_manager=drydock_state, + ingester=deckhand_ingester, + enabled_drivers=config.config_mgr.conf.plugins) + + status, site_design = Orchestrator.get_effective_site(orch, design_ref) + + message_list = Validator.valid_platform_selection( + site_design, orchestrator=orch) + + for m in message_list: + print(m.to_dict()) + + msg = message_list[0].to_dict() + assert 'invalid kernel lts' in msg.get('message') + assert msg.get('error') + assert len(message_list) == 1 diff --git a/tests/yaml_samples/deckhand_fullsite.yaml b/tests/yaml_samples/deckhand_fullsite.yaml index 04a5675a..555178cf 100644 --- a/tests/yaml_samples/deckhand_fullsite.yaml +++ b/tests/yaml_samples/deckhand_fullsite.yaml @@ -264,8 +264,8 @@ data: fstype: 'xfs' mount_options: 'defaults' platform: - image: ubuntu_16.04 - kernel: generic + image: 'xenial' + kernel: 'ga-16.04' kernel_params: quiet: true console: ttyS2 diff --git a/tests/yaml_samples/invalid_kernel.yaml b/tests/yaml_samples/invalid_kernel.yaml new file mode 100644 index 00000000..5cb23f9f --- /dev/null +++ b/tests/yaml_samples/invalid_kernel.yaml @@ -0,0 +1,329 @@ +#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. +#################### +# +# bootstrap_seed.yaml - Site server design definition for physical layer +# +#################### +# version the schema in this file so consumers can rationally parse it +--- +schema: 'drydock/Region/v1' +metadata: + schema: 'metadata/Document/v1' + name: 'sitename' + storagePolicy: 'cleartext' + labels: + application: 'drydock' +data: + tag_definitions: + - tag: 'test' + definition_type: 'lshw_xpath' + definition: "//node[@id=\"display\"]/'clock units=\"Hz\"' > 1000000000" + authorized_keys: + - | + ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDENeyO5hLPbLLQRZ0oafTYWs1ieo5Q+XgyZQs51Ju + jDGc8lKlWsg1/6yei2JewKMgcwG2Buu1eqU92Xn1SvMZLyt9GZURuBkyjcfVc/8GiU5QP1Of8B7CV0c + kfUpHWYJ17olTzT61Hgz10ioicBF6cjgQrLNcyn05xoaJHD2Vpf8Unxzi0YzA2e77yRqBo9jJVRaX2q + wUJuZrzb62x3zw8Knz6GGSZBn8xRKLaw1SKFpd1hwvL62GfqX5ZBAT1AYTZP1j8GcAoK8AFVn193SEU + vjSdUFa+RNWuJhkjBRfylJczIjTIFb5ls0jpbA3bMA9DE7lFKVQl6vVwFmiIVBI1 samplekey +--- +schema: 'drydock/NetworkLink/v1' +metadata: + schema: 'metadata/Document/v1' + name: oob + storagePolicy: 'cleartext' + labels: + application: 'drydock' +data: + bonding: + mode: disabled + mtu: 1500 + linkspeed: 100full + trunking: + mode: disabled + default_network: oob + allowed_networks: + - oob +--- +schema: 'drydock/NetworkLink/v1' +metadata: + schema: 'metadata/Document/v1' + name: pxe + storagePolicy: 'cleartext' + labels: + application: 'drydock' +data: + bonding: + mode: disabled + mtu: 1500 + linkspeed: auto + trunking: + mode: disabled + default_network: pxe + allowed_networks: + - pxe +--- +schema: 'drydock/NetworkLink/v1' +metadata: + schema: 'metadata/Document/v1' + name: gp + storagePolicy: 'cleartext' + labels: + application: 'drydock' +data: + bonding: + mode: 802.3ad + hash: layer3+4 + peer_rate: slow + mtu: 9000 + linkspeed: auto + trunking: + mode: 802.1q + default_network: mgmt + allowed_networks: + - public + - mgmt +--- +schema: 'drydock/Rack/v1' +metadata: + schema: 'metadata/Document/v1' + name: rack1 + storagePolicy: 'cleartext' + labels: + application: 'drydock' +data: + tor_switches: + switch01name: + mgmt_ip: 1.1.1.1 + sdn_api_uri: polo+https://polo-api.web.att.com/switchmgmt?switch=switch01name + switch02name: + mgmt_ip: 1.1.1.2 + sdn_api_uri: polo+https://polo-api.web.att.com/switchmgmt?switch=switch02name + location: + clli: HSTNTXMOCG0 + grid: EG12 + local_networks: + - pxe-rack1 +--- +schema: 'drydock/Network/v1' +metadata: + schema: 'metadata/Document/v1' + name: oob + storagePolicy: 'cleartext' + labels: + application: 'drydock' +data: + cidr: 172.16.100.0/24 + ranges: + - type: static + start: 172.16.100.15 + end: 172.16.100.254 + dns: + domain: ilo.sitename.att.com + servers: 172.16.100.10 +--- +schema: 'drydock/Network/v1' +metadata: + schema: 'metadata/Document/v1' + name: pxe + storagePolicy: 'cleartext' + labels: + application: 'drydock' +data: + dhcp_relay: + self_ip: 172.16.0.4 + upstream_target: 172.16.5.5 + mtu: 1500 + cidr: 172.16.0.0/24 + ranges: + - type: dhcp + start: 172.16.0.5 + end: 172.16.0.254 + dns: + domain: admin.sitename.att.com + servers: 172.16.0.10 +--- +schema: 'drydock/Network/v1' +metadata: + schema: 'metadata/Document/v1' + name: mgmt + storagePolicy: 'cleartext' + labels: + application: 'drydock' +data: + vlan: '100' + mtu: 1500 + cidr: 172.16.1.0/24 + ranges: + - type: static + start: 172.16.1.15 + end: 172.16.1.254 + routes: + - subnet: 0.0.0.0/0 + gateway: 172.16.1.1 + metric: 10 + dns: + domain: mgmt.sitename.example.com + servers: 172.16.1.9,172.16.1.10 +--- +schema: 'drydock/Network/v1' +metadata: + schema: 'metadata/Document/v1' + name: private + storagePolicy: 'cleartext' + labels: + application: 'drydock' +data: + vlan: '101' + mtu: 9000 + cidr: 172.16.2.0/24 + ranges: + - type: static + start: 172.16.2.15 + end: 172.16.2.254 + dns: + domain: priv.sitename.example.com + servers: 172.16.2.9,172.16.2.10 +--- +schema: 'drydock/Network/v1' +metadata: + schema: 'metadata/Document/v1' + name: public + storagePolicy: 'cleartext' + labels: + application: 'drydock' +data: + vlan: '102' + mtu: 1500 + cidr: 172.16.3.0/24 + ranges: + - type: static + start: 172.16.3.15 + end: 172.16.3.254 + routes: + - subnet: 0.0.0.0/0 + gateway: 172.16.3.1 + metric: 10 + dns: + domain: sitename.example.com + servers: 8.8.8.8 +--- +schema: 'drydock/HostProfile/v1' +metadata: + schema: 'metadata/Document/v1' + name: defaults + storagePolicy: 'cleartext' + labels: + application: 'drydock' +data: + hardware_profile: HPGen9v3 + oob: + type: ipmi + network: oob + account: admin + credential: admin + storage: + physical_devices: + sda: + labels: + role: rootdisk + partitions: + - name: root + size: 39% + bootable: true + filesystem: + mountpoint: '/' + fstype: 'ext4' + mount_options: 'defaults' + - name: boot + size: 42% + bootable: false + filesystem: + mountpoint: '/boot' + fstype: 'ext4' + mount_options: 'defaults' + sdb: + volume_group: 'log_vg' + volume_groups: + log_vg: + logical_volumes: + - name: 'log_lv' + size: '25%' + filesystem: + mountpoint: '/var/log' + fstype: 'xfs' + mount_options: 'defaults' + platform: + image: xenial + kernel: lts + kernel_params: + quiet: true + console: ttyS2 + metadata: + owner_data: + foo: bar +--- +schema: 'drydock/BaremetalNode/v1' +metadata: + schema: 'metadata/Document/v1' + name: controller01 + storagePolicy: 'cleartext' + labels: + application: 'drydock' +data: + host_profile: defaults + interfaces: + bond0: + networks: + - '!private' + addressing: + - network: pxe + address: dhcp + - network: mgmt + address: 172.16.1.20 + - network: public + address: 172.16.3.20 + - network: oob + address: 172.16.100.20 + metadata: + rack: rack1 +--- +schema: 'drydock/HardwareProfile/v1' +metadata: + schema: 'metadata/Document/v1' + name: HPGen9v3 + storagePolicy: 'cleartext' + labels: + application: 'drydock' +data: + vendor: HP + generation: '8' + hw_version: '3' + bios_version: '2.2.3' + boot_mode: bios + bootstrap_protocol: pxe + pxe_interface: 0 + device_aliases: + prim_nic01: + address: '0000:00:03.0' + dev_type: '82540EM Gigabit Ethernet Controller' + bus_type: 'pci' + prim_nic02: + address: '0000:00:04.0' + dev_type: '82540EM Gigabit Ethernet Controller' + bus_type: 'pci' + primary_boot: + address: '2:0.0.0' + dev_type: 'VBOX HARDDISK' + bus_type: 'scsi' diff --git a/tests/yaml_samples/validation.yaml b/tests/yaml_samples/validation.yaml index 829d964f..7f38685b 100644 --- a/tests/yaml_samples/validation.yaml +++ b/tests/yaml_samples/validation.yaml @@ -264,8 +264,8 @@ data: fstype: 'xfs' mount_options: 'defaults' platform: - image: ubuntu_16.04 - kernel: generic + image: 'xenial' + kernel: 'ga-16.04' kernel_params: quiet: true console: ttyS2