diff --git a/doc/source/user/proxies/baremetal.rst b/doc/source/user/proxies/baremetal.rst index 350757cd8..d8978c0ff 100644 --- a/doc/source/user/proxies/baremetal.rst +++ b/doc/source/user/proxies/baremetal.rst @@ -77,6 +77,14 @@ Volume Target Operations create_volume_target, update_volume_target, patch_volume_target, delete_volume_target +Deploy Template Operations +^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autoclass:: openstack.baremetal.v1._proxy.Proxy + :noindex: + :members: deploy_templates, get_deploy_template, + create_deploy_template, update_deploy_template, + patch_deploy_template, delete_deploy_template + Utilities --------- diff --git a/doc/source/user/resources/baremetal/index.rst b/doc/source/user/resources/baremetal/index.rst index 7ccb9a292..1b284f304 100644 --- a/doc/source/user/resources/baremetal/index.rst +++ b/doc/source/user/resources/baremetal/index.rst @@ -12,3 +12,4 @@ Baremetal Resources v1/allocation v1/volume_connector v1/volume_target + v1/deploy_templates diff --git a/doc/source/user/resources/baremetal/v1/deploy_templates.rst b/doc/source/user/resources/baremetal/v1/deploy_templates.rst new file mode 100644 index 000000000..e55e63afa --- /dev/null +++ b/doc/source/user/resources/baremetal/v1/deploy_templates.rst @@ -0,0 +1,13 @@ +openstack.baremetal.v1.deploy_templates +======================================= + +.. automodule:: openstack.baremetal.v1.deploy_templates + +The DeployTemplate Class +------------------------- + +The ``DeployTemplate`` class inherits +from :class:`~openstack.resource.Resource`. + +.. autoclass:: openstack.baremetal.v1.deploy_templates.DeployTemplate + :members: diff --git a/openstack/baremetal/v1/_proxy.py b/openstack/baremetal/v1/_proxy.py index 356d8ee40..b395dbc6d 100644 --- a/openstack/baremetal/v1/_proxy.py +++ b/openstack/baremetal/v1/_proxy.py @@ -19,6 +19,7 @@ from openstack.baremetal.v1 import port as _port from openstack.baremetal.v1 import port_group as _portgroup from openstack.baremetal.v1 import volume_connector as _volumeconnector from openstack.baremetal.v1 import volume_target as _volumetarget +from openstack.baremetal.v1 import deploy_templates as _deploytemplates from openstack import exceptions from openstack import proxy from openstack import utils @@ -1309,3 +1310,108 @@ class Proxy(proxy.Proxy): """ return self._delete(_volumetarget.VolumeTarget, volume_target, ignore_missing=ignore_missing) + + def deploy_templates(self, details=False, **query): + """Retrieve a generator of deploy_templates. + + :param details: A boolean indicating whether the detailed information + for every deploy_templates should be returned. + :param dict query: Optional query parameters to be sent to + restrict the deploy_templates to be returned. + + :returns: A generator of Deploy templates instances. + """ + if details: + query['detail'] = True + return _deploytemplates.DeployTemplate.list(self, **query) + + def create_deploy_template(self, **attrs): + """Create a new deploy_template from attributes. + + :param dict attrs: Keyword arguments that will be used to create a + :class:`~openstack.baremetal.v1.deploy_templates.DeployTemplate`. + + :returns: The results of deploy_template creation. + :rtype: + :class:`~openstack.baremetal.v1.deploy_templates.DeployTemplate`. + """ + return self._create(_deploytemplates.DeployTemplate, **attrs) + + def update_deploy_template(self, deploy_template, **attrs): + """Update a deploy_template. + + :param deploy_template: Either the ID of a deploy_template, + or an instance of + :class:`~openstack.baremetal.v1.deploy_templates.DeployTemplate`. + :param dict attrs: The attributes to update on + the deploy_template represented + by the ``deploy_template`` parameter. + + :returns: The updated deploy_template. + :rtype::class: + `~openstack.baremetal.v1.deploy_templates.DeployTemplate` + """ + return self._update(_deploytemplates.DeployTemplate, + deploy_template, **attrs) + + def delete_deploy_template(self, deploy_template, + ignore_missing=True): + """Delete a deploy_template. + + :param deploy_template:The value can be + either the ID of a deploy_template or a + :class:`~openstack.baremetal.v1.deploy_templates.DeployTemplate` + instance. + + :param bool ignore_missing: When set to ``False``, + an exception:class:`~openstack.exceptions.ResourceNotFound` + will be raised when the deploy_template + could not be found. + When set to ``True``, no + exception will be raised when attempting + to delete a non-existent + deploy_template. + + :returns: The instance of the deploy_template which was deleted. + :rtype::class: + `~openstack.baremetal.v1.deploy_templates.DeployTemplate`. + """ + + return self._delete(_deploytemplates.DeployTemplate, + deploy_template, ignore_missing=ignore_missing) + + def get_deploy_template(self, deploy_template, fields=None): + """Get a specific deployment template. + + :param deploy_template: The value can be the name or ID + of a deployment template + :class:`~openstack.baremetal.v1.deploy_templates.DeployTemplate` + instance. + + :param fields: Limit the resource fields to fetch. + + :returns: One + :class:`~openstack.baremetal.v1.deploy_templates.DeployTemplate` + :raises: :class:`~openstack.exceptions.ResourceNotFound` + when no deployment template matching the name or + ID could be found. + """ + return self._get_with_fields(_deploytemplates.DeployTemplate, + deploy_template, fields=fields) + + def patch_deploy_template(self, deploy_template, patch): + """Apply a JSON patch to the deploy_templates. + + :param deploy_templates: The value can be the ID of a + deploy_template or a + :class:`~openstack.baremetal.v1.deploy_templates.DeployTemplate` + instance. + + :param patch: JSON patch to apply. + + :returns: The updated deploy_template. + :rtype::class: + `~openstack.baremetal.v1.deploy_templates.DeployTemplate` + """ + return self._get_resource(_deploytemplates.DeployTemplate, + deploy_template).patch(self, patch) diff --git a/openstack/baremetal/v1/deploy_templates.py b/openstack/baremetal/v1/deploy_templates.py new file mode 100644 index 000000000..3c9e40420 --- /dev/null +++ b/openstack/baremetal/v1/deploy_templates.py @@ -0,0 +1,51 @@ +# 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 DeployTemplate(_common.ListMixin, resource.Resource): + + resources_key = 'deploy_templates' + base_path = '/deploy_templates' + + # 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}, + ) + + # Deploy Templates is available since 1.55 + _max_microversion = '1.55' + name = resource.Body('name') + #: Timestamp at which the deploy_template 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 deploy_template. + steps = resource.Body('steps', type=list) + #: Timestamp at which the deploy_template was last updated. + updated_at = resource.Body('updated_at') + #: The UUID of the resource. + id = resource.Body('uuid', alternate_id=True) diff --git a/openstack/tests/functional/baremetal/base.py b/openstack/tests/functional/baremetal/base.py index 8dc00044d..948bc9dcb 100644 --- a/openstack/tests/functional/baremetal/base.py +++ b/openstack/tests/functional/baremetal/base.py @@ -84,3 +84,16 @@ class BaseBaremetalTest(base.BaseFunctionalTest): self.conn.baremetal.delete_volume_target(volume_target.id, ignore_missing=True)) return volume_target + + def create_deploy_template(self, **kwargs): + """Create a new deploy_template from attributes. + """ + + deploy_template = self.conn.baremetal.create_deploy_template( + **kwargs) + + self.addCleanup( + lambda: self.conn.baremetal.delete_deploy_template( + deploy_template.id, + ignore_missing=True)) + return deploy_template diff --git a/openstack/tests/functional/baremetal/test_baremetal_deploy_templates.py b/openstack/tests/functional/baremetal/test_baremetal_deploy_templates.py new file mode 100644 index 000000000..eb59056dc --- /dev/null +++ b/openstack/tests/functional/baremetal/test_baremetal_deploy_templates.py @@ -0,0 +1,190 @@ +# 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 import exceptions +from openstack.tests.functional.baremetal import base + + +class TestBareMetalDeployTemplate(base.BaseBaremetalTest): + + min_microversion = '1.55' + + def setUp(self): + super(TestBareMetalDeployTemplate, self).setUp() + + def test_baremetal_deploy_create_get_delete(self): + steps = [ + { + "interface": "bios", + "step": "apply_configuration", + "args": { + "settings": [ + { + "name": "LogicalProc", + "value": "Enabled" + } + ] + }, + "priority": 150 + } + ] + deploy_template = self.create_deploy_template( + name='CUSTOM_DEPLOY_TEMPLATE', + steps=steps) + loaded = self.conn.baremetal.get_deploy_template( + deploy_template.id) + self.assertEqual(loaded.id, deploy_template.id) + self.conn.baremetal.delete_deploy_template(deploy_template, + ignore_missing=False) + self.assertRaises(exceptions.ResourceNotFound, + self.conn.baremetal.get_deploy_template, + deploy_template.id) + + def test_baremetal_deploy_template_list(self): + steps = [ + { + "interface": "bios", + "step": "apply_configuration", + "args": { + "settings": [ + { + "name": "LogicalProc", + "value": "Enabled" + } + ] + }, + "priority": 150 + } + ] + + deploy_template1 = self.create_deploy_template( + name='CUSTOM_DEPLOY_TEMPLATE1', + steps=steps) + deploy_template2 = self.create_deploy_template( + name='CUSTOM_DEPLOY_TEMPLATE2', + steps=steps) + deploy_templates = self.conn.baremetal.deploy_templates() + ids = [template.id for template in deploy_templates] + self.assertIn(deploy_template1.id, ids) + self.assertIn(deploy_template2.id, ids) + + deploy_templates_with_details = self.conn.baremetal.deploy_templates( + details=True) + for dp in deploy_templates_with_details: + self.assertIsNotNone(dp.id) + self.assertIsNotNone(dp.name) + + deploy_tempalte_with_fields = self.conn.baremetal.deploy_templates( + fields=['uuid']) + for dp in deploy_tempalte_with_fields: + self.assertIsNotNone(dp.id) + self.assertIsNone(dp.name) + + def test_baremetal_deploy_list_update_delete(self): + steps = [ + { + "interface": "bios", + "step": "apply_configuration", + "args": { + "settings": [ + { + "name": "LogicalProc", + "value": "Enabled" + } + ] + }, + "priority": 150 + } + ] + deploy_template = self.create_deploy_template( + name='CUSTOM_DEPLOY_TEMPLATE4', + steps=steps) + self.assertFalse(deploy_template.extra) + deploy_template.extra = {'answer': 42} + + deploy_template = self.conn.baremetal.update_deploy_template( + deploy_template) + self.assertEqual({'answer': 42}, deploy_template.extra) + + deploy_template = self.conn.baremetal.get_deploy_template( + deploy_template.id) + + self.conn.baremetal.delete_deploy_template(deploy_template.id, + ignore_missing=False) + + def test_baremetal_deploy_update(self): + steps = [ + { + "interface": "bios", + "step": "apply_configuration", + "args": { + "settings": [ + { + "name": "LogicalProc", + "value": "Enabled" + } + ] + }, + "priority": 150 + } + ] + deploy_template = self.create_deploy_template( + name='CUSTOM_DEPLOY_TEMPLATE4', + steps=steps) + deploy_template.extra = {'answer': 42} + + deploy_template = self.conn.baremetal.update_deploy_template( + deploy_template) + self.assertEqual({'answer': 42}, deploy_template.extra) + + deploy_template = self.conn.baremetal.get_deploy_template( + deploy_template.id) + self.assertEqual({'answer': 42}, deploy_template.extra) + + def test_deploy_template_patch(self): + name = "CUSTOM_HYPERTHREADING_ON" + steps = [ + { + "interface": "bios", + "step": "apply_configuration", + "args": { + "settings": [ + { + "name": "LogicalProc", + "value": "Enabled" + } + ] + }, + "priority": 150 + } + ] + deploy_template = self.create_deploy_template( + name=name, + steps=steps) + deploy_template = self.conn.baremetal.patch_deploy_template( + deploy_template, dict(path='/extra/answer', op='add', value=42)) + self.assertEqual({'answer': 42}, deploy_template.extra) + self.assertEqual(name, + deploy_template.name) + + deploy_template = self.conn.baremetal.get_deploy_template( + deploy_template.id) + self.assertEqual({'answer': 42}, deploy_template.extra) + + def test_deploy_template_negative_non_existing(self): + uuid = "bbb45f41-d4bc-4307-8d1d-32f95ce1e920" + self.assertRaises(exceptions.ResourceNotFound, + self.conn.baremetal.get_deploy_template, uuid) + self.assertRaises(exceptions.ResourceNotFound, + self.conn.baremetal.delete_deploy_template, uuid, + ignore_missing=False) + self.assertIsNone(self.conn.baremetal.delete_deploy_template(uuid)) diff --git a/openstack/tests/unit/baremetal/v1/test_deploy_templates.py b/openstack/tests/unit/baremetal/v1/test_deploy_templates.py new file mode 100644 index 000000000..0b826f425 --- /dev/null +++ b/openstack/tests/unit/baremetal/v1/test_deploy_templates.py @@ -0,0 +1,75 @@ +# 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.tests.unit import base + +from openstack.baremetal.v1 import deploy_templates + +FAKE = { + "created_at": "2016-08-18T22:28:48.643434+11:11", + "extra": {}, + "links": [ + { + "href": """http://10.60.253.180:6385/v1/deploy_templates + /bbb45f41-d4bc-4307-8d1d-32f95ce1e920""", + "rel": "self" + }, + { + "href": """http://10.60.253.180:6385/deploy_templates + /bbb45f41-d4bc-4307-8d1d-32f95ce1e920""", + "rel": "bookmark" + } + ], + "name": "CUSTOM_HYPERTHREADING_ON", + "steps": [ + { + "args": { + "settings": [ + { + "name": "LogicalProc", + "value": "Enabled" + } + ] + }, + "interface": "bios", + "priority": 150, + "step": "apply_configuration" + } + ], + "updated_at": None, + "uuid": "bbb45f41-d4bc-4307-8d1d-32f95ce1e920" +} + + +class DeployTemplates(base.TestCase): + + def test_basic(self): + sot = deploy_templates.DeployTemplate() + self.assertIsNone(sot.resource_key) + self.assertEqual('deploy_templates', sot.resources_key) + self.assertEqual('/deploy_templates', 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 = deploy_templates.DeployTemplate(**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['updated_at'], sot.updated_at) + self.assertEqual(FAKE['uuid'], sot.id) diff --git a/releasenotes/notes/ironic-deploy-template-support-fa56005365ed6e4d.yaml b/releasenotes/notes/ironic-deploy-template-support-fa56005365ed6e4d.yaml new file mode 100644 index 000000000..d8b13ea7a --- /dev/null +++ b/releasenotes/notes/ironic-deploy-template-support-fa56005365ed6e4d.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Support Deploy Templates for Ironic API \ No newline at end of file