Add support for the runbooks feature
Adds support for managing runbooks within the OpenStack SDK. Related-Change: #922142 Change-Id: Ia590918c2e4bd629724c2e50146a904099858477
This commit is contained in:
@@ -94,6 +94,10 @@ CHANGE_BOOT_MODE_VERSION = '1.76'
|
||||
FIRMWARE_VERSION = '1.86'
|
||||
"""API version in which firmware components of a node can be accessed"""
|
||||
|
||||
RUNBOOKS_VERSION = '1.92'
|
||||
"""API version in which a runbook can be used in place of arbitrary steps
|
||||
for provisioning"""
|
||||
|
||||
|
||||
class Resource(resource.Resource):
|
||||
base_path: str
|
||||
|
@@ -100,8 +100,8 @@ class Node(_common.Resource):
|
||||
is_maintenance='maintenance',
|
||||
)
|
||||
|
||||
# Ability to have a firmware_interface on a node.
|
||||
_max_microversion = '1.87'
|
||||
# Ability to run predefined sets of steps on a node using runbooks.
|
||||
_max_microversion = '1.92'
|
||||
|
||||
# Properties
|
||||
#: The UUID of the allocation associated with this node. Added in API
|
||||
@@ -207,9 +207,13 @@ class Node(_common.Resource):
|
||||
#: A string to be used by external schedulers to identify this node as a
|
||||
#: unit of a specific type of resource. Added in API microversion 1.21.
|
||||
resource_class = resource.Body("resource_class")
|
||||
#: A string represents the current service step being executed upon.
|
||||
#: A string representing the current service step being executed upon.
|
||||
#: Added in API microversion 1.87.
|
||||
service_step = resource.Body("service_step")
|
||||
#: A string representing the uuid or logical name of a runbook as an
|
||||
#: alternative to providing ``clean_steps`` or ``service_steps``.
|
||||
#: Added in API microversion 1.92.
|
||||
runbook = resource.Body("runbook")
|
||||
#: A string indicating the shard this node belongs to. Added in API
|
||||
#: microversion 1,82.
|
||||
shard = resource.Body("shard")
|
||||
@@ -407,6 +411,7 @@ class Node(_common.Resource):
|
||||
timeout=None,
|
||||
deploy_steps=None,
|
||||
service_steps=None,
|
||||
runbook=None,
|
||||
):
|
||||
"""Run an action modifying this node's provision state.
|
||||
|
||||
@@ -431,6 +436,7 @@ class Node(_common.Resource):
|
||||
and ``rebuild`` target.
|
||||
:param service_steps: Service steps to execute, only valid for
|
||||
``service`` target.
|
||||
:param ``runbook``: UUID or logical name of a runbook.
|
||||
|
||||
:return: This :class:`Node` instance.
|
||||
:raises: ValueError if ``config_drive``, ``clean_steps``,
|
||||
@@ -460,6 +466,31 @@ class Node(_common.Resource):
|
||||
version = self._assert_microversion_for(session, 'commit', version)
|
||||
|
||||
body = {'target': target}
|
||||
if runbook:
|
||||
version = self._assert_microversion_for(
|
||||
session, 'commit', _common.RUNBOOKS_VERSION
|
||||
)
|
||||
|
||||
if clean_steps is not None:
|
||||
raise ValueError(
|
||||
'Please provide either clean steps or a '
|
||||
'runbook, but not both.'
|
||||
)
|
||||
if service_steps is not None:
|
||||
raise ValueError(
|
||||
'Please provide either service steps or a '
|
||||
'runbook, but not both.'
|
||||
)
|
||||
|
||||
if target != 'clean' and target != 'service':
|
||||
msg = (
|
||||
'A runbook can only be provided when setting target '
|
||||
'provision state to any of "[clean, service]"'
|
||||
)
|
||||
raise ValueError(msg)
|
||||
|
||||
body['runbook'] = runbook
|
||||
|
||||
if config_drive:
|
||||
if target not in ('active', 'rebuild'):
|
||||
raise ValueError(
|
||||
|
54
openstack/baremetal/v1/runbooks.py
Normal file
54
openstack/baremetal/v1/runbooks.py
Normal file
@@ -0,0 +1,54 @@
|
||||
# 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.
|
||||
|
||||
from openstack.baremetal.v1 import _common
|
||||
from openstack import resource
|
||||
|
||||
|
||||
class Runbook(_common.Resource):
|
||||
resources_key = 'runbooks'
|
||||
base_path = '/runbooks'
|
||||
|
||||
# capabilities
|
||||
allow_create = True
|
||||
allow_fetch = True
|
||||
allow_commit = True
|
||||
allow_delete = True
|
||||
allow_list = True
|
||||
allow_patch = True
|
||||
commit_method = 'PATCH'
|
||||
commit_jsonpatch = True
|
||||
|
||||
_query_mapping = resource.QueryParameters(
|
||||
'detail',
|
||||
fields={'type': _common.fields_type},
|
||||
)
|
||||
|
||||
# Runbooks is available since 1.92
|
||||
_max_microversion = '1.92'
|
||||
name = resource.Body('name')
|
||||
#: Timestamp at which the runbook was created.
|
||||
created_at = resource.Body('created_at')
|
||||
#: A set of one or more arbitrary metadata key and value pairs.
|
||||
extra = resource.Body('extra')
|
||||
#: A list of relative links. Includes the self and bookmark links.
|
||||
links = resource.Body('links', type=list)
|
||||
#: A set of physical information of the runbook.
|
||||
steps = resource.Body('steps', type=list)
|
||||
#: Indicates whether the runbook is publicly accessible.
|
||||
public = resource.Body('public', type=bool)
|
||||
#: The name or ID of the project that owns the runbook.
|
||||
owner = resource.Body('owner', type=str)
|
||||
#: Timestamp at which the runbook was last updated.
|
||||
updated_at = resource.Body('updated_at')
|
||||
#: The UUID of the resource.
|
||||
id = resource.Body('uuid', alternate_id=True)
|
@@ -83,6 +83,7 @@ FAKE = {
|
||||
"service_step": {},
|
||||
"secure_boot": True,
|
||||
"shard": "TestShard",
|
||||
"runbook": None,
|
||||
"states": [
|
||||
{
|
||||
"href": "http://127.0.0.1:6385/v1/nodes/<NODE_ID>/states",
|
||||
@@ -161,6 +162,7 @@ class TestNode(base.TestCase):
|
||||
self.assertEqual(FAKE['resource_class'], sot.resource_class)
|
||||
self.assertEqual(FAKE['service_step'], sot.service_step)
|
||||
self.assertEqual(FAKE['secure_boot'], sot.is_secure_boot)
|
||||
self.assertEqual(FAKE['runbook'], sot.runbook)
|
||||
self.assertEqual(FAKE['states'], sot.states)
|
||||
self.assertEqual(
|
||||
FAKE['target_provision_state'], sot.target_provision_state
|
||||
@@ -438,6 +440,36 @@ class TestNodeSetProvisionState(base.TestCase):
|
||||
retriable_status_codes=_common.RETRIABLE_STATUS_CODES,
|
||||
)
|
||||
|
||||
def test_set_provision_state_clean_runbook(self):
|
||||
runbook = 'CUSTOM_AWESOME'
|
||||
result = self.node.set_provision_state(
|
||||
self.session, 'clean', runbook=runbook
|
||||
)
|
||||
|
||||
self.assertIs(result, self.node)
|
||||
self.session.put.assert_called_once_with(
|
||||
'nodes/%s/states/provision' % self.node.id,
|
||||
json={'target': 'clean', 'runbook': runbook},
|
||||
headers=mock.ANY,
|
||||
microversion='1.92',
|
||||
retriable_status_codes=_common.RETRIABLE_STATUS_CODES,
|
||||
)
|
||||
|
||||
def test_set_provision_state_service_runbook(self):
|
||||
runbook = 'CUSTOM_AWESOME'
|
||||
result = self.node.set_provision_state(
|
||||
self.session, 'service', runbook=runbook
|
||||
)
|
||||
|
||||
self.assertIs(result, self.node)
|
||||
self.session.put.assert_called_once_with(
|
||||
'nodes/%s/states/provision' % self.node.id,
|
||||
json={'target': 'service', 'runbook': runbook},
|
||||
headers=mock.ANY,
|
||||
microversion='1.92',
|
||||
retriable_status_codes=_common.RETRIABLE_STATUS_CODES,
|
||||
)
|
||||
|
||||
|
||||
@mock.patch.object(node.Node, '_translate_response', mock.Mock())
|
||||
@mock.patch.object(node.Node, '_get_session', lambda self, x: x)
|
||||
|
73
openstack/tests/unit/baremetal/v1/test_runbooks.py
Normal file
73
openstack/tests/unit/baremetal/v1/test_runbooks.py
Normal file
@@ -0,0 +1,73 @@
|
||||
# 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.
|
||||
|
||||
from openstack.baremetal.v1 import runbooks
|
||||
from openstack.tests.unit import base
|
||||
|
||||
|
||||
FAKE = {
|
||||
"created_at": "2024-08-18T22:28:48.643434+11:11",
|
||||
"extra": {},
|
||||
"links": [
|
||||
{
|
||||
"href": """http://10.60.253.180:6385/v1/runbooks
|
||||
/bbb45f41-d4bc-4307-8d1d-32f95ce1e920""",
|
||||
"rel": "self",
|
||||
},
|
||||
{
|
||||
"href": """http://10.60.253.180:6385/runbooks
|
||||
/bbb45f41-d4bc-4307-8d1d-32f95ce1e920""",
|
||||
"rel": "bookmark",
|
||||
},
|
||||
],
|
||||
"name": "CUSTOM_AWESOME",
|
||||
"public": False,
|
||||
"owner": "blah",
|
||||
"steps": [
|
||||
{
|
||||
"args": {
|
||||
"settings": [{"name": "LogicalProc", "value": "Enabled"}]
|
||||
},
|
||||
"interface": "bios",
|
||||
"order": 1,
|
||||
"step": "apply_configuration",
|
||||
}
|
||||
],
|
||||
"updated_at": None,
|
||||
"uuid": "32f95ce1-4307-d4bc-8d1d-e920bbb45f41",
|
||||
}
|
||||
|
||||
|
||||
class Runbooks(base.TestCase):
|
||||
def test_basic(self):
|
||||
sot = runbooks.Runbook()
|
||||
self.assertIsNone(sot.resource_key)
|
||||
self.assertEqual('runbooks', sot.resources_key)
|
||||
self.assertEqual('/runbooks', sot.base_path)
|
||||
self.assertTrue(sot.allow_create)
|
||||
self.assertTrue(sot.allow_fetch)
|
||||
self.assertTrue(sot.allow_commit)
|
||||
self.assertTrue(sot.allow_delete)
|
||||
self.assertTrue(sot.allow_list)
|
||||
self.assertEqual('PATCH', sot.commit_method)
|
||||
|
||||
def test_instantiate(self):
|
||||
sot = runbooks.Runbook(**FAKE)
|
||||
self.assertEqual(FAKE['steps'], sot.steps)
|
||||
self.assertEqual(FAKE['created_at'], sot.created_at)
|
||||
self.assertEqual(FAKE['extra'], sot.extra)
|
||||
self.assertEqual(FAKE['links'], sot.links)
|
||||
self.assertEqual(FAKE['name'], sot.name)
|
||||
self.assertEqual(FAKE['public'], sot.public)
|
||||
self.assertEqual(FAKE['owner'], sot.owner)
|
||||
self.assertEqual(FAKE['updated_at'], sot.updated_at)
|
||||
self.assertEqual(FAKE['uuid'], sot.id)
|
@@ -0,0 +1,6 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Adds support for runbooks; an API feature that enables project members
|
||||
to self-serve maintenance tasks via predefined step lists in lieu of
|
||||
an arbitrary list of clean/service steps.
|
Reference in New Issue
Block a user