Deploy templates: add API tests
Adds tests for the deploy templates API added in 1.55. Also fixes an issue where the microversion fixture was not used in the resource cleanup, so new resources would see a 404 and not be deleted. Fixing this uncovered an issue in the volume tests where volume connectors and targets could not be deleted due to being associated with a powered on node. The simplest fix was to move node deletion before the volume connector and target in resource cleanup. Change-Id: I09d9a21e1ea5512c0140e818b0ca3de501870c12 Depends-On: https://review.openstack.org/631845 Story: 1722275 Task: 28679
This commit is contained in:
parent
94f4c20926
commit
6f2e72c455
|
@ -113,6 +113,11 @@ class BaremetalClient(base.BaremetalClient):
|
|||
"""List all registered allocations."""
|
||||
return self._list_request('allocations', **kwargs)
|
||||
|
||||
@base.handle_errors
|
||||
def list_deploy_templates(self, **kwargs):
|
||||
"""List all deploy templates."""
|
||||
return self._list_request('deploy_templates', **kwargs)
|
||||
|
||||
@base.handle_errors
|
||||
def show_node(self, uuid, api_version=None):
|
||||
"""Gets a specific node.
|
||||
|
@ -234,6 +239,14 @@ class BaremetalClient(base.BaremetalClient):
|
|||
uri = '/nodes/%s/allocation' % node_ident
|
||||
return self._show_request('nodes', uuid=None, uri=uri)
|
||||
|
||||
def show_deploy_template(self, deploy_template_ident):
|
||||
"""Gets a specific deploy template.
|
||||
|
||||
:param deploy_template_ident: Name or UUID of deploy template.
|
||||
:return: Serialized deploy template as a dictionary.
|
||||
"""
|
||||
return self._show_request('deploy_templates', deploy_template_ident)
|
||||
|
||||
@base.handle_errors
|
||||
def create_node(self, chassis_id=None, **kwargs):
|
||||
"""Create a baremetal node with the specified parameters.
|
||||
|
@ -386,6 +399,26 @@ class BaremetalClient(base.BaremetalClient):
|
|||
|
||||
return self._create_request('volume/targets', volume_target)
|
||||
|
||||
@base.handle_errors
|
||||
def create_deploy_template(self, name, **kwargs):
|
||||
"""Create a deploy template with the specified parameters.
|
||||
|
||||
:param name: The name of the deploy template.
|
||||
:param kwargs:
|
||||
steps: deploy steps of the template.
|
||||
uuid: UUID of the deploy template. Optional.
|
||||
extra: meta-data of the deploy template. Optional.
|
||||
:return: A tuple with the server response and the created deploy
|
||||
template.
|
||||
"""
|
||||
deploy_template = {'name': name}
|
||||
|
||||
for arg in ('extra', 'steps', 'uuid'):
|
||||
if arg in kwargs:
|
||||
deploy_template[arg] = kwargs[arg]
|
||||
|
||||
return self._create_request('deploy_templates', deploy_template)
|
||||
|
||||
@base.handle_errors
|
||||
def delete_node(self, uuid):
|
||||
"""Deletes a node having the specified UUID.
|
||||
|
@ -444,6 +477,15 @@ class BaremetalClient(base.BaremetalClient):
|
|||
"""
|
||||
return self._delete_request('volume/targets', volume_target_ident)
|
||||
|
||||
@base.handle_errors
|
||||
def delete_deploy_template(self, deploy_template_ident):
|
||||
"""Deletes a deploy template having the specified name or UUID.
|
||||
|
||||
:param deploy_template_ident: Name or UUID of the deploy template.
|
||||
:return: A tuple with the server response and the response body.
|
||||
"""
|
||||
return self._delete_request('deploy_templates', deploy_template_ident)
|
||||
|
||||
@base.handle_errors
|
||||
def update_node(self, uuid, patch=None, **kwargs):
|
||||
"""Update the specified node.
|
||||
|
@ -533,6 +575,20 @@ class BaremetalClient(base.BaremetalClient):
|
|||
|
||||
return self._patch_request('volume/targets', uuid, patch)
|
||||
|
||||
@base.handle_errors
|
||||
def update_deploy_template(self, deploy_template_ident, patch):
|
||||
"""Update the specified deploy template.
|
||||
|
||||
:param deploy_template_ident: Name or UUID of the deploy template.
|
||||
:param patch: List of dicts representing json patches. Each dict
|
||||
has keys 'path', 'op' and 'value'; to update a field.
|
||||
:return: A tuple with the server response and the updated deploy
|
||||
template.
|
||||
"""
|
||||
|
||||
return self._patch_request('deploy_templates', deploy_template_ident,
|
||||
patch)
|
||||
|
||||
@base.handle_errors
|
||||
def set_node_power_state(self, node_uuid, state):
|
||||
"""Set power state of the specified node.
|
||||
|
|
|
@ -19,6 +19,7 @@ from tempest import test
|
|||
|
||||
from ironic_tempest_plugin import clients
|
||||
from ironic_tempest_plugin.common import waiters
|
||||
from ironic_tempest_plugin.services.baremetal import base
|
||||
from ironic_tempest_plugin.tests.api.admin import api_microversion_fixture
|
||||
|
||||
CONF = config.CONF
|
||||
|
@ -33,8 +34,8 @@ SUPPORTED_DRIVERS = ['fake', 'fake-hardware']
|
|||
|
||||
# NOTE(jroll): resources must be deleted in a specific order, this list
|
||||
# defines the resource types to clean up, and the correct order.
|
||||
RESOURCE_TYPES = ['port', 'portgroup', 'volume_connector', 'volume_target',
|
||||
'node', 'chassis']
|
||||
RESOURCE_TYPES = ['port', 'portgroup', 'node', 'volume_connector',
|
||||
'volume_target', 'chassis', 'deploy_template']
|
||||
|
||||
|
||||
def creates(resource):
|
||||
|
@ -110,6 +111,9 @@ class BaseBaremetalTest(api_version_utils.BaseMicroversionTest,
|
|||
@classmethod
|
||||
def resource_cleanup(cls):
|
||||
"""Ensure that all created objects get destroyed."""
|
||||
# Use the requested microversion for cleanup to ensure we can delete
|
||||
# resources.
|
||||
base.set_baremetal_api_microversion(cls.request_microversion)
|
||||
try:
|
||||
for node in cls.deployed_nodes:
|
||||
try:
|
||||
|
@ -130,6 +134,7 @@ class BaseBaremetalTest(api_version_utils.BaseMicroversionTest,
|
|||
for u in uuids:
|
||||
delete_method(u, ignore_errors=lib_exc.NotFound)
|
||||
finally:
|
||||
base.reset_baremetal_api_microversion()
|
||||
super(BaseBaremetalTest, cls).resource_cleanup()
|
||||
|
||||
def _assertExpected(self, expected, actual):
|
||||
|
@ -320,6 +325,19 @@ class BaseBaremetalTest(api_version_utils.BaseMicroversionTest,
|
|||
|
||||
return resp, body
|
||||
|
||||
@classmethod
|
||||
@creates('deploy_template')
|
||||
def create_deploy_template(cls, name, **kwargs):
|
||||
"""Wrapper utility for creating test deploy template.
|
||||
|
||||
:param name: The name of the deploy template.
|
||||
:return: A tuple with the server response and the created deploy
|
||||
template.
|
||||
"""
|
||||
resp, body = cls.client.create_deploy_template(name=name, **kwargs)
|
||||
|
||||
return resp, body
|
||||
|
||||
@classmethod
|
||||
def delete_chassis(cls, chassis_id):
|
||||
"""Deletes a chassis having the specified UUID.
|
||||
|
@ -411,6 +429,21 @@ class BaseBaremetalTest(api_version_utils.BaseMicroversionTest,
|
|||
|
||||
return resp
|
||||
|
||||
@classmethod
|
||||
def delete_deploy_template(cls, deploy_template_ident):
|
||||
"""Deletes a deploy template having the specified name or UUID.
|
||||
|
||||
:param deploy_template_ident: Name or UUID of the deploy template.
|
||||
:return: Server response.
|
||||
"""
|
||||
resp, body = cls.client.delete_deploy_template(deploy_template_ident)
|
||||
|
||||
if deploy_template_ident in cls.created_objects['deploy_template']:
|
||||
cls.created_objects['deploy_template'].remove(
|
||||
deploy_template_ident)
|
||||
|
||||
return resp
|
||||
|
||||
def validate_self_link(self, resource, uuid, link):
|
||||
"""Check whether the given self link formatted correctly."""
|
||||
expected_link = "{base}/{pref}/{res}/{uuid}".format(
|
||||
|
|
|
@ -0,0 +1,340 @@
|
|||
# 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.
|
||||
|
||||
import copy
|
||||
|
||||
from tempest.lib.common.utils import data_utils
|
||||
from tempest.lib import decorators
|
||||
from tempest.lib import exceptions as lib_exc
|
||||
|
||||
from ironic_tempest_plugin.tests.api.admin import api_microversion_fixture
|
||||
from ironic_tempest_plugin.tests.api.admin import base
|
||||
|
||||
|
||||
EXAMPLE_STEPS = [{
|
||||
'interface': 'bios',
|
||||
'step': 'apply_configuration',
|
||||
'args': {},
|
||||
'priority': 10
|
||||
}]
|
||||
|
||||
|
||||
def _get_random_trait():
|
||||
return data_utils.rand_name('CUSTOM', '').replace('-', '_')
|
||||
|
||||
|
||||
class TestDeployTemplates(base.BaseBaremetalTest):
|
||||
"""Tests for deploy templates."""
|
||||
|
||||
min_microversion = '1.55'
|
||||
|
||||
def setUp(self):
|
||||
super(TestDeployTemplates, self).setUp()
|
||||
self.name = _get_random_trait()
|
||||
self.steps = copy.deepcopy(EXAMPLE_STEPS)
|
||||
_, self.template = self.create_deploy_template(self.name,
|
||||
steps=self.steps)
|
||||
|
||||
@decorators.idempotent_id('8f39c794-4e6f-42fc-bfcb-e1f6eafff2e9')
|
||||
def test_create_deploy_template_specifying_uuid(self):
|
||||
name = _get_random_trait()
|
||||
uuid = data_utils.rand_uuid()
|
||||
|
||||
_, template = self.create_deploy_template(name=name, steps=self.steps,
|
||||
uuid=uuid)
|
||||
|
||||
_, body = self.client.show_deploy_template(uuid)
|
||||
self._assertExpected(template, body)
|
||||
|
||||
@decorators.idempotent_id('7ac8fdd5-928b-4fbc-8341-3ebaad9def5e')
|
||||
def test_delete_deploy_template(self):
|
||||
self.delete_deploy_template(self.template['uuid'])
|
||||
|
||||
self.assertRaises(lib_exc.NotFound, self.client.show_deploy_template,
|
||||
self.template['uuid'])
|
||||
|
||||
@decorators.idempotent_id('f424dd67-46c3-4169-b8f0-7e0c18b70437')
|
||||
def test_show_deploy_template(self):
|
||||
_, template = self.client.show_deploy_template(self.template['uuid'])
|
||||
self._assertExpected(self.template, template)
|
||||
self.assertIn('name', template)
|
||||
self.assertIn('steps', template)
|
||||
self.assertIn('uuid', template)
|
||||
self.assertIn('extra', template)
|
||||
|
||||
@decorators.idempotent_id('2fd98e9a-10ce-405a-a32c-0d6079766183')
|
||||
def test_show_deploy_template_with_links(self):
|
||||
_, template = self.client.show_deploy_template(self.template['uuid'])
|
||||
self.assertIn('links', template.keys())
|
||||
self.assertEqual(2, len(template['links']))
|
||||
self.assertIn(template['uuid'], template['links'][0]['href'])
|
||||
|
||||
@decorators.idempotent_id('cec2a01d-07af-4062-a8b0-9a1703f65bcf')
|
||||
def test_list_deploy_templates(self):
|
||||
_, body = self.client.list_deploy_templates()
|
||||
self.assertIn(self.template['uuid'],
|
||||
[i['uuid'] for i in body['deploy_templates']])
|
||||
# Verify self links.
|
||||
for template in body['deploy_templates']:
|
||||
self.validate_self_link('deploy_templates', template['uuid'],
|
||||
template['links'][0]['href'])
|
||||
|
||||
@decorators.idempotent_id('89aea2bf-c094-445f-b869-9fd56d1dfe5a')
|
||||
def test_list_with_limit(self):
|
||||
for i in range(2):
|
||||
name = _get_random_trait()
|
||||
self.create_deploy_template(name, steps=self.steps)
|
||||
|
||||
_, body = self.client.list_deploy_templates(limit=3)
|
||||
|
||||
next_marker = body['deploy_templates'][-1]['uuid']
|
||||
self.assertIn(next_marker, body['next'])
|
||||
|
||||
@decorators.idempotent_id('c09c917e-c4d2-4148-9b6a-459bd126ed7c')
|
||||
def test_list_deploy_templates_detail(self):
|
||||
uuids = [
|
||||
self.create_deploy_template(_get_random_trait(), steps=self.steps)
|
||||
[1]['uuid'] for i in range(0, 5)]
|
||||
|
||||
_, body = self.client.list_deploy_templates(detail=True)
|
||||
|
||||
templates_dict = dict((template['uuid'], template)
|
||||
for template in body['deploy_templates']
|
||||
if template['uuid'] in uuids)
|
||||
|
||||
for uuid in uuids:
|
||||
self.assertIn(uuid, templates_dict)
|
||||
template = templates_dict[uuid]
|
||||
self.assertIn('name', template)
|
||||
self.assertIn('steps', template)
|
||||
self.assertIn('uuid', template)
|
||||
self.assertIn('extra', template)
|
||||
# Verify self link.
|
||||
self.validate_self_link('deploy_templates', template['uuid'],
|
||||
template['links'][0]['href'])
|
||||
|
||||
@decorators.idempotent_id('a6cf1ade-e19a-41e2-b151-13ecf0d8f08c')
|
||||
def test_update_deploy_template_replace(self):
|
||||
new_name = _get_random_trait()
|
||||
new_steps = [{
|
||||
'interface': 'raid',
|
||||
'step': 'create_configuration',
|
||||
'args': {},
|
||||
'priority': 10,
|
||||
}]
|
||||
|
||||
patch = [{'path': '/name', 'op': 'replace', 'value': new_name},
|
||||
{'path': '/steps', 'op': 'replace', 'value': new_steps}]
|
||||
|
||||
self.client.update_deploy_template(self.template['uuid'], patch)
|
||||
|
||||
_, body = self.client.show_deploy_template(self.template['uuid'])
|
||||
self.assertEqual(new_name, body['name'])
|
||||
self.assertEqual(new_steps, body['steps'])
|
||||
|
||||
@decorators.idempotent_id('bb168c63-452b-4065-9a81-77853ca9540a')
|
||||
def test_update_deploy_template_add(self):
|
||||
new_steps = [
|
||||
{
|
||||
'interface': 'bios',
|
||||
'step': 'cache_bios_settings',
|
||||
'args': {},
|
||||
'priority': 20
|
||||
},
|
||||
{
|
||||
'interface': 'bios',
|
||||
'step': 'factory_reset',
|
||||
'args': {},
|
||||
'priority': 30
|
||||
},
|
||||
]
|
||||
|
||||
patch = [{'path': '/steps/1', 'op': 'add', 'value': new_steps[0]},
|
||||
{'path': '/steps/2', 'op': 'add', 'value': new_steps[1]}]
|
||||
|
||||
self.client.update_deploy_template(self.template['uuid'], patch)
|
||||
|
||||
_, body = self.client.show_deploy_template(self.template['uuid'])
|
||||
self.assertEqual(self.steps + new_steps, body['steps'])
|
||||
|
||||
@decorators.idempotent_id('2aa204a2-1d50-48fd-8b76-d2ed15586d50')
|
||||
def test_update_deploy_template_mixed_ops(self):
|
||||
new_name = _get_random_trait()
|
||||
new_steps = [
|
||||
{
|
||||
'interface': 'bios',
|
||||
'step': 'apply_configuration',
|
||||
'args': {},
|
||||
'priority': 20
|
||||
},
|
||||
{
|
||||
'interface': 'bios',
|
||||
'step': 'apply_configuration',
|
||||
'args': {},
|
||||
'priority': 30
|
||||
},
|
||||
]
|
||||
|
||||
patch = [{'path': '/name', 'op': 'replace', 'value': new_name},
|
||||
{'path': '/steps/0', 'op': 'replace', 'value': new_steps[0]},
|
||||
{'path': '/steps/0', 'op': 'remove'},
|
||||
{'path': '/steps/0', 'op': 'add', 'value': new_steps[1]}]
|
||||
|
||||
self.client.update_deploy_template(self.template['uuid'], patch)
|
||||
|
||||
_, body = self.client.show_deploy_template(self.template['uuid'])
|
||||
self.assertEqual(new_name, body['name'])
|
||||
self.assertEqual([new_steps[1]], body['steps'])
|
||||
|
||||
|
||||
class TestDeployTemplatesOldAPI(base.BaseBaremetalTest):
|
||||
"""Negative tests for deploy templates using an old API version."""
|
||||
|
||||
max_microversion = '1.54'
|
||||
|
||||
def setUp(self):
|
||||
super(TestDeployTemplatesOldAPI, self).setUp()
|
||||
self.useFixture(
|
||||
api_microversion_fixture.APIMicroversionFixture(
|
||||
self.max_microversion)
|
||||
)
|
||||
|
||||
@decorators.attr(type=['negative'])
|
||||
@decorators.idempotent_id('e9481a0d-23e0-4757-bc11-c3c9ab9d3839')
|
||||
def test_create_deploy_template_old_api(self):
|
||||
self.assertRaises(lib_exc.NotFound,
|
||||
self.create_deploy_template,
|
||||
name=_get_random_trait(), steps=EXAMPLE_STEPS)
|
||||
|
||||
@decorators.attr(type=['negative'])
|
||||
@decorators.idempotent_id('0d3af2aa-ba53-4c8a-92d4-91f9b4179fe7')
|
||||
def test_update_deploy_template_old_api(self):
|
||||
patch = [{'path': '/name', 'op': 'replace',
|
||||
'value': _get_random_trait()}]
|
||||
|
||||
self.assertRaises(lib_exc.NotFound,
|
||||
self.client.update_deploy_template,
|
||||
_get_random_trait(), patch)
|
||||
|
||||
@decorators.attr(type=['negative'])
|
||||
@decorators.idempotent_id('1646b1e5-ab81-45a8-9ea0-30444a4dcaa2')
|
||||
def test_delete_deploy_template_old_api(self):
|
||||
self.assertRaises(lib_exc.NotFound,
|
||||
self.client.delete_deploy_template,
|
||||
_get_random_trait())
|
||||
|
||||
@decorators.attr(type=['negative'])
|
||||
@decorators.idempotent_id('819480ac-f36a-4402-b1d5-504d7cf55b1f')
|
||||
def test_list_deploy_templates_old_api(self):
|
||||
self.assertRaises(lib_exc.NotFound,
|
||||
self.client.list_deploy_templates)
|
||||
|
||||
|
||||
class TestDeployTemplatesNegative(base.BaseBaremetalTest):
|
||||
"""Negative tests for deploy templates."""
|
||||
|
||||
min_microversion = '1.55'
|
||||
|
||||
def setUp(self):
|
||||
super(TestDeployTemplatesNegative, self).setUp()
|
||||
self.useFixture(
|
||||
api_microversion_fixture.APIMicroversionFixture(
|
||||
self.min_microversion)
|
||||
)
|
||||
self.steps = EXAMPLE_STEPS
|
||||
|
||||
@decorators.attr(type=['negative'])
|
||||
@decorators.idempotent_id('a4085c08-e718-4c2f-a796-0e115b659243')
|
||||
def test_create_deploy_template_invalid_name(self):
|
||||
name = 'invalid-name'
|
||||
self.assertRaises(lib_exc.BadRequest,
|
||||
self.create_deploy_template, name=name,
|
||||
steps=self.steps)
|
||||
|
||||
@decorators.attr(type=['negative'])
|
||||
@decorators.idempotent_id('6390acc4-9490-4b23-8b4c-41888a78c9b7')
|
||||
def test_create_deploy_template_duplicated_deploy_template_name(self):
|
||||
name = _get_random_trait()
|
||||
self.create_deploy_template(name=name, steps=self.steps)
|
||||
self.assertRaises(lib_exc.Conflict, self.create_deploy_template,
|
||||
name=name, steps=self.steps)
|
||||
|
||||
@decorators.attr(type=['negative'])
|
||||
@decorators.idempotent_id('ed3f0cec-13e8-4175-9fdb-d129e7b7fe10')
|
||||
def test_create_deploy_template_no_mandatory_field_name(self):
|
||||
self.assertRaises(lib_exc.BadRequest, self.create_deploy_template,
|
||||
name=None, steps=self.steps)
|
||||
|
||||
@decorators.attr(type=['negative'])
|
||||
@decorators.idempotent_id('af5dd0df-d903-463f-9535-9e4e9d6fd576')
|
||||
def test_create_deploy_template_no_mandatory_field_steps(self):
|
||||
self.assertRaises(lib_exc.BadRequest, self.create_deploy_template,
|
||||
name=_get_random_trait())
|
||||
|
||||
@decorators.attr(type=['negative'])
|
||||
@decorators.idempotent_id('cbd33bc5-7602-40b7-943e-3e92217567a3')
|
||||
def test_create_deploy_template_malformed_steps(self):
|
||||
steps = {'key': 'value'}
|
||||
self.assertRaises(lib_exc.BadRequest, self.create_deploy_template,
|
||||
name=_get_random_trait(), steps=steps)
|
||||
|
||||
@decorators.attr(type=['negative'])
|
||||
@decorators.idempotent_id('2a562fca-f377-4a6e-b332-37ee82d3a983')
|
||||
def test_create_deploy_template_malformed_deploy_template_uuid(self):
|
||||
uuid = 'malformed:uuid'
|
||||
self.assertRaises(lib_exc.BadRequest, self.create_deploy_template,
|
||||
name=_get_random_trait(), steps=self.steps,
|
||||
uuid=uuid)
|
||||
|
||||
@decorators.attr(type=['negative'])
|
||||
@decorators.idempotent_id('2c006994-88ca-43b7-b605-897d479229d9')
|
||||
def test_show_deploy_template_nonexistent(self):
|
||||
self.assertRaises(lib_exc.NotFound, self.client.show_deploy_template,
|
||||
data_utils.rand_uuid())
|
||||
|
||||
@decorators.attr(type=['negative'])
|
||||
@decorators.idempotent_id('5a815f37-f015-4d68-9b22-099504f74805')
|
||||
def test_update_deploy_template_remove_mandatory_field_steps(self):
|
||||
name = _get_random_trait()
|
||||
_, template = self.create_deploy_template(name=name, steps=self.steps)
|
||||
|
||||
# Removing one item from the collection
|
||||
self.assertRaises(lib_exc.BadRequest,
|
||||
self.client.update_deploy_template,
|
||||
template['uuid'],
|
||||
[{'path': '/steps/0', 'op': 'remove'}])
|
||||
|
||||
# Removing the collection
|
||||
self.assertRaises(lib_exc.BadRequest,
|
||||
self.client.update_deploy_template,
|
||||
template['uuid'],
|
||||
[{'path': '/steps', 'op': 'remove'}])
|
||||
|
||||
@decorators.attr(type=['negative'])
|
||||
@decorators.idempotent_id('ee852ebb-a601-4593-9d59-063fcbc8f964')
|
||||
def test_update_deploy_template_remove_mandatory_field_name(self):
|
||||
name = _get_random_trait()
|
||||
_, template = self.create_deploy_template(name=name, steps=self.steps)
|
||||
self.assertRaises(lib_exc.BadRequest,
|
||||
self.client.update_deploy_template,
|
||||
template['uuid'],
|
||||
[{'path': '/name', 'op': 'remove'}])
|
||||
|
||||
@decorators.attr(type=['negative'])
|
||||
@decorators.idempotent_id('e59bf38d-272f-4490-b21e-9db217f11378')
|
||||
def test_update_deploy_template_replace_empty_name(self):
|
||||
name = _get_random_trait()
|
||||
_, template = self.create_deploy_template(name=name, steps=self.steps)
|
||||
self.assertRaises(lib_exc.BadRequest,
|
||||
self.client.update_deploy_template,
|
||||
template['uuid'],
|
||||
[{'path': '/name', 'op': 'replace', 'value': ''}])
|
Loading…
Reference in New Issue