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
This commit is contained in:
Scott Hussey 2018-01-18 12:49:09 -06:00
parent 1804203ea1
commit 4a1367a39a
17 changed files with 620 additions and 31 deletions

3
.gitignore vendored
View File

@ -101,3 +101,6 @@ ENV/
# IDEA IDE
.idea/
# VIM
.*.swp

View File

@ -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 []

View File

@ -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)

View File

@ -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

View File

@ -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}

View File

@ -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

View File

@ -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)
]

View File

@ -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:

View File

@ -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:

View File

@ -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,
]

View File

@ -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",

View File

@ -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)

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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'

View File

@ -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