Boot Action document definition

Boot actions are defined by YAML documents in the site topology.
This PS defines the schema for those documents and adds a representation
to the Drydock ORM and ingester.

- Create JSON schema document for boot action documents
- Model for BootAction objects
- Parsing for BootAction documents

Change-Id: I154807e8400389ff94a596ffd1fb2cab0efa128b
This commit is contained in:
Scott Hussey 2017-10-20 21:07:14 -05:00
parent 575e7acbb8
commit 3eac4d4d53
8 changed files with 327 additions and 0 deletions

View File

@ -25,6 +25,7 @@ import drydock_provisioner.objects.node as node
import drydock_provisioner.objects.hostprofile as hostprofile
import drydock_provisioner.objects.promenade as prom
import drydock_provisioner.objects.rack as rack
import drydock_provisioner.objects.bootaction as bootaction
class Ingester(object):
@ -119,4 +120,6 @@ class Ingester(object):
design_data.add_promenade_config(m)
elif type(m) is rack.Rack:
design_data.add_rack(m)
elif type(m) is bootaction.BootAction:
design_data.add_bootaction(m)
return status, design_data

View File

@ -382,6 +382,55 @@ class YamlIngester(IngesterPlugin):
return model
def process_drydock_bootaction(self, name, data):
"""Process the data/spec section of a BootAction document.
:param name: the document name attribute
:Param data: the dictionary of the parsed data/spec section
"""
model = objects.BootAction()
model.name = name
model.source = hd_fields.ModelSource.Designed
assets = data.get('assets')
model.asset_list = objects.BootActionAssetList()
for a in assets:
ba = self.process_bootaction_asset(a)
model.asset_list.append(ba)
node_filter = data.get('node_filter', None)
if node_filter is not None:
nfs = self.process_bootaction_nodefilter(node_filter)
model.node_filter = nfs
return model
def process_bootaction_asset(self, asset_dict):
"""Process a dictionary representing a BootAction Data Asset.
:param asset_dict: dictionary representing the bootaction asset
"""
model = objects.BootActionAsset(**asset_dict)
return model
def process_bootaction_nodefilter(self, nf):
"""Process a dictionary representing a BootAction NodeFilter Set.
:param nf: dictionary representing the bootaction nodefilter set.
"""
model = objects.NodeFilterSet()
model.filter_set_type = nf.get('filter_set_type', None)
model.filter_set = []
for nf in nf.get('filter_set', []):
nf_model = objects.NodeFilter(**nf)
model.filter_set.append(nf_model)
return model
def process_drydock_node(self, name, data):
"""Process the data/spec section of a BaremetalNode document.
@ -610,4 +659,5 @@ class YamlIngester(IngesterPlugin):
'HardwareProfile': process_drydock_hwprofile,
'HostProfile': process_drydock_hostprofile,
'BaremetalNode': process_drydock_node,
'BootAction': process_drydock_bootaction,
}

View File

@ -29,6 +29,7 @@ def register_all():
importlib.import_module('drydock_provisioner.objects.site')
importlib.import_module('drydock_provisioner.objects.promenade')
importlib.import_module('drydock_provisioner.objects.rack')
importlib.import_module('drydock_provisioner.objects.bootaction')
importlib.import_module('drydock_provisioner.objects.task')

View File

@ -0,0 +1,117 @@
# 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.
"""Object models for BootActions."""
import oslo_versionedobjects.fields as ovo_fields
import drydock_provisioner.objects.base as base
import drydock_provisioner.objects.fields as hd_fields
@base.DrydockObjectRegistry.register
class BootAction(base.DrydockPersistentObject, base.DrydockObject):
VERSION = '1.0'
fields = {
'name':
ovo_fields.StringField(),
'source':
hd_fields.ModelSourceField(nullable=False),
'asset_list':
ovo_fields.ObjectField('BootActionAssetList', nullable=False),
'node_filter':
ovo_fields.ObjectField('NodeFilterSet', nullable=True),
}
def __init__(self, **kwargs):
super().__init__(**kwargs)
# NetworkLink keyed by name
def get_id(self):
return self.get_name()
def get_name(self):
return self.name
@base.DrydockObjectRegistry.register
class BootActionList(base.DrydockObjectListBase, base.DrydockObject):
VERSION = '1.0'
fields = {
'objects': ovo_fields.ListOfObjectsField('BootAction'),
}
@base.DrydockObjectRegistry.register
class BootActionAsset(base.DrydockObject):
VERSION = '1.0'
fields = {
'type': ovo_fields.StringField(nullable=True),
'path': ovo_fields.StringField(nullable=True),
'location': ovo_fields.StringField(nullable=True),
'data': ovo_fields.StringField(nullable=True),
'location_pipeline': ovo_fields.ListOfStringsField(nullable=True),
'data_pipeline': ovo_fields.ListOfStringsField(nullable=True),
'permissions': ovo_fields.IntegerField(nullable=True),
}
def __init__(self, **kwargs):
super().__init__(**kwargs)
@base.DrydockObjectRegistry.register
class BootActionAssetList(base.DrydockObjectListBase, base.DrydockObject):
VERSION = '1.0'
fields = {
'objects': ovo_fields.ListOfObjectsField('BootActionAsset'),
}
@base.DrydockObjectRegistry.register
class NodeFilterSet(base.DrydockObject):
VERSION = '1.0'
fields = {
'filter_set_type': ovo_fields.StringField(nullable=False),
'filter_set': ovo_fields.ListOfObjectsField('NodeFilter'),
}
def __init__(self, **kwargs):
super().__init__(**kwargs)
@base.DrydockObjectRegistry.register
class NodeFilter(base.DrydockObject):
VERSION = '1.0'
fields = {
'filter_type': ovo_fields.StringField(nullable=False),
'node_names': ovo_fields.ListOfStringsField(nullable=True),
'node_tags': ovo_fields.ListOfStringsField(nullable=True),
'node_labels': ovo_fields.DictOfStringsField(nullable=True),
'rack_names': ovo_fields.ListOfStringsField(nullable=True),
'rack_labels': ovo_fields.DictOfStringsField(nullable=True),
}
def __init__(self, **kwargs):
super().__init__(**kwargs)

View File

@ -148,6 +148,8 @@ class SiteDesign(base.DrydockPersistentObject, base.DrydockObject):
ovo_fields.ObjectField('PromenadeConfigList', nullable=True),
'racks':
ovo_fields.ObjectField('RackList', nullable=True),
'bootactions':
ovo_fields.ObjectField('BootActionList', nullable=True),
}
def __init__(self, **kwargs):
@ -218,6 +220,27 @@ class SiteDesign(base.DrydockPersistentObject, base.DrydockObject):
raise errors.DesignError(
"Rack %s not found in design state" % rack_key)
def add_bootaction(self, new_ba):
"""Add a bootaction definition to this site design.
:param new_ba: instance of BootAction to add to the design
"""
if self.bootactions is None:
self.bootactions = objects.BootActionList()
self.bootactions.append(new_ba)
def get_bootaction(self, ba_key):
"""Select a boot action from this site design with the matchkey key.
:param ba_key: Value should match the ``get_id()`` value of the BootAction returned
"""
for ba in self.bootactions:
if ba.get_id() == ba_key:
return ba
raise errors.DesignError(
"BootAction %s not found in design state" % ba_key)
def add_host_profile(self, new_host_profile):
if new_host_profile is None:
raise errors.DesignError("Invalid HostProfile model")

View File

@ -0,0 +1,89 @@
---
schema: 'deckhand/DataSchema/v1'
metadata:
schema: metadata/Control/v1
name: drydock/BootAction/v1
labels:
application: drydock
data:
$schema: 'http://json-schema.org/schema#'
id: 'http://att.com/att-comdev/drydock/bootaction.yaml'
type: 'object'
additionalProperties: false
properties:
assets:
type: 'array'
items:
type: 'object'
additionalProperties: false
properties:
path:
type: 'string'
pattern: '^/.+'
location:
type: 'string'
type:
type: 'string'
enum:
- 'unit'
- 'file'
- 'pkg_list'
data:
type: 'string'
location_pipeline:
type: 'array'
items:
type: 'string'
enum:
- 'template'
data_pipeline:
type: 'array'
items:
type: 'string'
enum:
- 'base64_encode'
- 'template'
- 'base64_decode'
permissions:
type: 'integer'
required:
- 'type'
node_filter:
type: 'object'
additionalProperties: false
properties:
filter_set_type:
type: 'string'
enum:
- 'intersection'
- 'union'
filter_set:
type: 'array'
items:
type: 'object'
additionalProperties: false
properties:
filter_type:
type: 'string'
enum:
- 'intersection'
- 'union'
node_names:
type: 'array'
items:
type: 'string'
node_tags:
type: 'array'
items:
type: 'string'
node_labels:
type: 'object'
additionalProperties: true
rack_names:
type: 'array'
items:
type: 'string'
rack_labels:
type: 'object'
additionalProperties: true
...

View File

@ -0,0 +1,26 @@
---
apiVersion: 'drydock/v1'
kind: BootAction
metadata:
name: helloworld
region: sitename
date: 17-FEB-2017
author: Scott Hussey
spec:
assets:
- path: /var/tmp/hello.sh
type: file
permissions: 555
data: |
IyEvYmluL2Jhc2gKCmVjaG8gJ0hlbGxvIFdvcmxkIScK
data_pipeline:
- base64_decode
- path: /lib/systemd/system/hello.service
type: unit
data: |
W1VuaXRdCkRlc2NyaXB0aW9uPUhlbGxvIFdvcmxkCgpbU2VydmljZV0KVHlwZT1vbmVzaG90CkV4
ZWNTdGFydD0vdmFyL3RtcC9oZWxsby5zaAoKW0luc3RhbGxdCldhbnRlZEJ5PW11bHRpLXVzZXIu
dGFyZ2V0Cg==
data_pipeline:
- base64_decode
...

View File

@ -0,0 +1,18 @@
data:
assets:
- path: /var/tmp/hello.sh
type: file
permissions: 555
data: |
IyEvYmluL2Jhc2gKCmVjaG8gJ0hlbGxvIFdvcmxkIScK
data_pipeline:
- foo
- path: hello.service
type: unit
data: |
W1VuaXRdCkRlc2NyaXB0aW9uPUhlbGxvIFdvcmxkCgpbU2VydmljZV0KVHlwZT1vbmVzaG90CkV4
ZWNTdGFydD0vdmFyL3RtcC9oZWxsby5zaAoKW0luc3RhbGxdCldhbnRlZEJ5PW11bHRpLXVzZXIu
dGFyZ2V0Cg==
data_pipeline:
- base64_decode