RPC service for software config/deployment
These RPC service methods are wrappers over the DB API. Implements: blueprint hot-software-config-rest Change-Id: I2c2e53f633cca30533b9190ebccc0d6eddcfb21f
This commit is contained in:
parent
6cef9c42e9
commit
d5cb8b461c
|
@ -280,3 +280,41 @@ def format_validate_parameter(param):
|
|||
res[api.PARAM_ALLOWED_PATTERN] = c.pattern
|
||||
|
||||
return res
|
||||
|
||||
|
||||
def format_software_config(sc):
|
||||
if sc is None:
|
||||
return
|
||||
result = {
|
||||
api.SOFTWARE_CONFIG_ID: sc.id,
|
||||
api.SOFTWARE_CONFIG_NAME: sc.name,
|
||||
api.SOFTWARE_CONFIG_GROUP: sc.group,
|
||||
api.SOFTWARE_CONFIG_CONFIG: sc.config,
|
||||
api.SOFTWARE_CONFIG_INPUTS: sc.io['inputs'],
|
||||
api.SOFTWARE_CONFIG_OUTPUTS: sc.io['outputs'],
|
||||
api.SOFTWARE_CONFIG_OPTIONS: sc.io['options']
|
||||
}
|
||||
return result
|
||||
|
||||
|
||||
def format_software_deployment(sd):
|
||||
if sd is None:
|
||||
return
|
||||
result = {
|
||||
api.SOFTWARE_DEPLOYMENT_ID: sd.id,
|
||||
api.SOFTWARE_DEPLOYMENT_SERVER_ID: sd.server_id,
|
||||
api.SOFTWARE_DEPLOYMENT_INPUT_VALUES: sd.input_values,
|
||||
api.SOFTWARE_DEPLOYMENT_OUTPUT_VALUES: sd.output_values,
|
||||
api.SOFTWARE_DEPLOYMENT_ACTION: sd.action,
|
||||
api.SOFTWARE_DEPLOYMENT_STATUS: sd.status,
|
||||
api.SOFTWARE_DEPLOYMENT_STATUS_REASON: sd.status_reason,
|
||||
api.SOFTWARE_DEPLOYMENT_SIGNAL_ID: sd.signal_id,
|
||||
api.SOFTWARE_DEPLOYMENT_CONFIG_ID: sd.config.id,
|
||||
api.SOFTWARE_CONFIG_CONFIG: sd.config.config,
|
||||
api.SOFTWARE_CONFIG_NAME: sd.config.name,
|
||||
api.SOFTWARE_CONFIG_GROUP: sd.config.group,
|
||||
api.SOFTWARE_CONFIG_INPUTS: sd.config.io['inputs'],
|
||||
api.SOFTWARE_CONFIG_OUTPUTS: sd.config.io['outputs'],
|
||||
api.SOFTWARE_CONFIG_OPTIONS: sd.config.io['options']
|
||||
}
|
||||
return result
|
||||
|
|
|
@ -967,3 +967,78 @@ class EngineService(service.Service):
|
|||
result = api.format_watch(wr)
|
||||
result[rpc_api.WATCH_STATE_VALUE] = state
|
||||
return result
|
||||
|
||||
@request_context
|
||||
def show_software_config(self, cnxt, config_id):
|
||||
sc = db_api.software_config_get(cnxt, config_id)
|
||||
return api.format_software_config(sc)
|
||||
|
||||
@request_context
|
||||
def create_software_config(self, cnxt, group, name, config,
|
||||
inputs, outputs, options):
|
||||
sc = db_api.software_config_create(cnxt, {
|
||||
'group': group,
|
||||
'name': name,
|
||||
'config': config,
|
||||
'io': {
|
||||
'inputs': inputs,
|
||||
'outputs': outputs,
|
||||
'options': options
|
||||
},
|
||||
'tenant': cnxt.tenant_id})
|
||||
return api.format_software_config(sc)
|
||||
|
||||
@request_context
|
||||
def delete_software_config(self, cnxt, config_id):
|
||||
db_api.software_config_delete(cnxt, config_id)
|
||||
|
||||
@request_context
|
||||
def list_software_deployments(self, cnxt, server_id):
|
||||
all_sd = db_api.software_deployment_get_all(cnxt, server_id)
|
||||
result = [api.format_software_deployment(sd) for sd in all_sd]
|
||||
return result
|
||||
|
||||
@request_context
|
||||
def show_software_deployment(self, cnxt, deployment_id):
|
||||
sd = db_api.software_deployment_get(cnxt, deployment_id)
|
||||
return api.format_software_deployment(sd)
|
||||
|
||||
@request_context
|
||||
def create_software_deployment(self, cnxt, server_id, config_id,
|
||||
input_values, signal_id, action, status,
|
||||
status_reason):
|
||||
sd = db_api.software_deployment_create(cnxt, {
|
||||
'config_id': config_id,
|
||||
'server_id': server_id,
|
||||
'input_values': input_values,
|
||||
'signal_id': signal_id,
|
||||
'tenant': cnxt.tenant_id,
|
||||
'action': action,
|
||||
'status': status,
|
||||
'status_reason': status_reason})
|
||||
return api.format_software_deployment(sd)
|
||||
|
||||
@request_context
|
||||
def update_software_deployment(self, cnxt, deployment_id, config_id,
|
||||
input_values, output_values, action,
|
||||
status, status_reason):
|
||||
update_data = {}
|
||||
if config_id:
|
||||
update_data['config_id'] = config_id
|
||||
if input_values:
|
||||
update_data['input_values'] = input_values
|
||||
if output_values:
|
||||
update_data['output_values'] = output_values
|
||||
if action:
|
||||
update_data['action'] = action
|
||||
if status:
|
||||
update_data['status'] = status
|
||||
if status_reason:
|
||||
update_data['status_reason'] = status_reason
|
||||
sd = db_api.software_deployment_update(cnxt,
|
||||
deployment_id, update_data)
|
||||
return api.format_software_deployment(sd)
|
||||
|
||||
@request_context
|
||||
def delete_software_deployment(self, cnxt, deployment_id):
|
||||
db_api.software_deployment_delete(cnxt, deployment_id)
|
||||
|
|
|
@ -179,3 +179,45 @@ VALIDATE_PARAM_TYPES = (
|
|||
'String', 'Number', 'CommaDelimitedList',
|
||||
'Json'
|
||||
)
|
||||
|
||||
SOFTWARE_CONFIG_KEYS = (
|
||||
SOFTWARE_CONFIG_ID,
|
||||
SOFTWARE_CONFIG_NAME,
|
||||
SOFTWARE_CONFIG_GROUP,
|
||||
SOFTWARE_CONFIG_CONFIG,
|
||||
SOFTWARE_CONFIG_INPUTS,
|
||||
SOFTWARE_CONFIG_OUTPUTS,
|
||||
SOFTWARE_CONFIG_OPTIONS,
|
||||
) = (
|
||||
'id',
|
||||
'name',
|
||||
'group',
|
||||
'config',
|
||||
'inputs',
|
||||
'outputs',
|
||||
'options',
|
||||
)
|
||||
|
||||
SOFTWARE_DEPLOYMENT_KEYS = (
|
||||
SOFTWARE_DEPLOYMENT_ID,
|
||||
SOFTWARE_DEPLOYMENT_CONFIG,
|
||||
SOFTWARE_DEPLOYMENT_CONFIG_ID,
|
||||
SOFTWARE_DEPLOYMENT_SERVER_ID,
|
||||
SOFTWARE_DEPLOYMENT_INPUT_VALUES,
|
||||
SOFTWARE_DEPLOYMENT_OUTPUT_VALUES,
|
||||
SOFTWARE_DEPLOYMENT_SIGNAL_ID,
|
||||
SOFTWARE_DEPLOYMENT_ACTION,
|
||||
SOFTWARE_DEPLOYMENT_STATUS,
|
||||
SOFTWARE_DEPLOYMENT_STATUS_REASON
|
||||
) = (
|
||||
'id',
|
||||
'config',
|
||||
'config_id',
|
||||
'server_id',
|
||||
'input_values',
|
||||
'output_values',
|
||||
'signal_id',
|
||||
'action',
|
||||
'status',
|
||||
'status_reason'
|
||||
)
|
||||
|
|
|
@ -350,3 +350,59 @@ class EngineClient(heat.openstack.common.rpc.proxy.RpcProxy):
|
|||
|
||||
def get_revision(self, ctxt):
|
||||
return self.call(ctxt, self.make_msg('get_revision'))
|
||||
|
||||
def show_software_config(self, cnxt, config_id):
|
||||
return self.call(cnxt, self.make_msg('show_software_config',
|
||||
config_id=config_id))
|
||||
|
||||
def create_software_config(self, cnxt, group, name, config,
|
||||
inputs=[], outputs=[], options={}):
|
||||
return self.call(cnxt, self.make_msg('create_software_config',
|
||||
group=group,
|
||||
name=name,
|
||||
config=config,
|
||||
inputs=inputs,
|
||||
outputs=outputs,
|
||||
options=options))
|
||||
|
||||
def delete_software_config(self, cnxt, config_id):
|
||||
return self.call(cnxt, self.make_msg('delete_software_config',
|
||||
config_id=config_id))
|
||||
|
||||
def list_software_deployments(self, cnxt, server_id=None):
|
||||
return self.call(cnxt, self.make_msg('list_software_deployments',
|
||||
server_id=server_id))
|
||||
|
||||
def show_software_deployment(self, cnxt, deployment_id):
|
||||
return self.call(cnxt, self.make_msg('show_software_deployment',
|
||||
deployment_id=deployment_id))
|
||||
|
||||
def create_software_deployment(self, cnxt, server_id, config_id=None,
|
||||
input_values={}, signal_id=None,
|
||||
action='INIT', status='COMPLETE',
|
||||
status_reason=''):
|
||||
return self.call(cnxt, self.make_msg('create_software_deployment',
|
||||
server_id=server_id,
|
||||
config_id=config_id,
|
||||
input_values=input_values,
|
||||
signal_id=signal_id,
|
||||
action=action,
|
||||
status=status,
|
||||
status_reason=status_reason))
|
||||
|
||||
def update_software_deployment(self, cnxt, deployment_id,
|
||||
config_id=None, input_values=None,
|
||||
output_values=None, action=None,
|
||||
status=None, status_reason=None):
|
||||
return self.call(cnxt, self.make_msg('update_software_deployment',
|
||||
deployment_id=deployment_id,
|
||||
config_id=config_id,
|
||||
input_values=input_values,
|
||||
output_values=output_values,
|
||||
action=action,
|
||||
status=status,
|
||||
status_reason=status_reason))
|
||||
|
||||
def delete_software_deployment(self, cnxt, deployment_id):
|
||||
return self.call(cnxt, self.make_msg('delete_software_deployment',
|
||||
deployment_id=deployment_id))
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
|
||||
import uuid
|
||||
|
||||
import mock
|
||||
|
||||
import heat.engine.api as api
|
||||
|
||||
from heat.common import template_format
|
||||
|
@ -633,3 +635,50 @@ class FormatValidateParameterTest(HeatTestCase):
|
|||
param = tmpl_params.params[self.param_name]
|
||||
param_formated = api.format_validate_parameter(param)
|
||||
self.assertEqual(self.expected, param_formated)
|
||||
|
||||
def _dummy_software_config(self):
|
||||
config = mock.Mock()
|
||||
config.name = 'config_mysql'
|
||||
config.group = 'Heat::Shell'
|
||||
config.config = '#!/bin/bash\n'
|
||||
config.id = str(uuid.uuid4())
|
||||
config.io = {
|
||||
'inputs': [{'name': 'bar'}],
|
||||
'outputs': [{'name': 'result'}],
|
||||
'options': {}
|
||||
}
|
||||
return config
|
||||
|
||||
def _dummy_software_deployment(self):
|
||||
config = self._dummy_software_config()
|
||||
deployment = mock.Mock()
|
||||
deployment.config = config
|
||||
deployment.id = str(uuid.uuid4())
|
||||
deployment.server_id = str(uuid.uuid4())
|
||||
deployment.input_values = {'bar': 'baaaaa'}
|
||||
deployment.output_values = {'result': '0'}
|
||||
deployment.action = 'INIT'
|
||||
deployment.status = 'COMPLETE'
|
||||
deployment.status_reason = None
|
||||
deployment.signal_id = None
|
||||
return deployment
|
||||
|
||||
def test_format_software_config(self):
|
||||
config = self._dummy_software_config()
|
||||
result = api.format_software_config(config)
|
||||
self.assertIsNotNone(result)
|
||||
self.assertEqual([{'name': 'bar'}], result['inputs'])
|
||||
self.assertEqual([{'name': 'result'}], result['outputs'])
|
||||
self.assertEqual({}, result['options'])
|
||||
|
||||
def test_format_software_deployment(self):
|
||||
deployment = self._dummy_software_deployment()
|
||||
result = api.format_software_deployment(deployment)
|
||||
self.assertIsNotNone(result)
|
||||
self.assertEqual(deployment.config.id, result['config_id'])
|
||||
self.assertEqual(
|
||||
deployment.config.io['inputs'], result['inputs'])
|
||||
self.assertEqual(
|
||||
deployment.config.io['outputs'], result['outputs'])
|
||||
self.assertEqual(
|
||||
deployment.config.io['options'], result['options'])
|
||||
|
|
|
@ -17,6 +17,7 @@ import functools
|
|||
from eventlet import greenpool
|
||||
import json
|
||||
import sys
|
||||
import uuid
|
||||
|
||||
import mock
|
||||
import mox
|
||||
|
@ -2321,3 +2322,182 @@ class StackServiceTest(HeatTestCase):
|
|||
stack_dependencies = stack.dependencies
|
||||
self.assertIsInstance(stack_dependencies, dependencies.Dependencies)
|
||||
self.assertEqual(2, len(stack_dependencies.graph()))
|
||||
|
||||
|
||||
class SoftwareConfigServiceTest(HeatTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(SoftwareConfigServiceTest, self).setUp()
|
||||
self.ctx = utils.dummy_context()
|
||||
|
||||
self.m.StubOutWithMock(service.EngineListener, 'start')
|
||||
service.EngineListener.start().AndReturn(None)
|
||||
self.m.ReplayAll()
|
||||
self.engine = service.EngineService('a-host', 'a-topic')
|
||||
utils.setup_dummy_db()
|
||||
|
||||
def _create_software_config(
|
||||
self, group='Heat::Shell', name='config_mysql', config=None,
|
||||
inputs=[], outputs=[], options={}):
|
||||
return self.engine.create_software_config(
|
||||
self.ctx, group, name, config, inputs, outputs, options)
|
||||
|
||||
def test_show_software_config(self):
|
||||
config_id = str(uuid.uuid4())
|
||||
self.assertIsNone(
|
||||
self.engine.show_software_config(self.ctx, config_id))
|
||||
config = self._create_software_config()
|
||||
config_id = config['id']
|
||||
self.assertEqual(
|
||||
config, self.engine.show_software_config(self.ctx, config_id))
|
||||
|
||||
def test_create_software_config(self):
|
||||
config = self._create_software_config()
|
||||
self.assertIsNotNone(config)
|
||||
config_id = config['id']
|
||||
config = self._create_software_config()
|
||||
self.assertNotEqual(config_id, config['id'])
|
||||
kwargs = {
|
||||
'group': 'Heat::Chef',
|
||||
'name': 'config_heat',
|
||||
'config': '...',
|
||||
'inputs': [{'name': 'mode'}],
|
||||
'outputs': [{'name': 'endpoint'}],
|
||||
'options': {}
|
||||
}
|
||||
config = self._create_software_config(**kwargs)
|
||||
config_id = config['id']
|
||||
config = self.engine.show_software_config(self.ctx, config_id)
|
||||
self.assertEqual(kwargs['group'], config['group'])
|
||||
self.assertEqual(kwargs['name'], config['name'])
|
||||
self.assertEqual(kwargs['config'], config['config'])
|
||||
self.assertEqual(kwargs['inputs'], config['inputs'])
|
||||
self.assertEqual(kwargs['outputs'], config['outputs'])
|
||||
self.assertEqual(kwargs['options'], config['options'])
|
||||
|
||||
def test_delete_software_config(self):
|
||||
config = self._create_software_config()
|
||||
self.assertIsNotNone(config)
|
||||
config_id = config['id']
|
||||
self.engine.delete_software_config(self.ctx, config_id)
|
||||
config = self.engine.show_software_config(self.ctx, config_id)
|
||||
self.assertIsNone(config)
|
||||
|
||||
def _create_software_deployment(self, config_id=None, input_values={},
|
||||
signal_id=None, action='INIT',
|
||||
status='COMPLETE', status_reason=''):
|
||||
if config_id is None:
|
||||
config = self._create_software_config()
|
||||
config_id = config['id']
|
||||
return self.engine.create_software_deployment(
|
||||
self.ctx, str(uuid.uuid4()), config_id, input_values, signal_id,
|
||||
action, status, status_reason)
|
||||
|
||||
def test_list_software_deployments(self):
|
||||
deployment = self._create_software_deployment()
|
||||
deployment_id = deployment['id']
|
||||
self.assertIsNotNone(deployment)
|
||||
deployments = self.engine.list_software_deployments(
|
||||
self.ctx, server_id=None)
|
||||
self.assertIsNotNone(deployments)
|
||||
deployment_ids = [x['id'] for x in deployments]
|
||||
self.assertIn(deployment_id, deployment_ids)
|
||||
self.assertIn(deployment, deployments)
|
||||
deployments = self.engine.list_software_deployments(
|
||||
self.ctx, server_id=str(uuid.uuid4()))
|
||||
self.assertEqual([], deployments)
|
||||
|
||||
def test_show_software_deployment(self):
|
||||
deployment_id = str(uuid.uuid4())
|
||||
self.assertIsNone(
|
||||
self.engine.show_software_deployment(self.ctx, deployment_id))
|
||||
deployment = self._create_software_deployment()
|
||||
self.assertIsNotNone(deployment)
|
||||
deployment_id = deployment['id']
|
||||
self.assertEqual(
|
||||
deployment,
|
||||
self.engine.show_software_deployment(self.ctx, deployment_id))
|
||||
|
||||
def test_create_software_deployment(self):
|
||||
kwargs = {
|
||||
'group': 'Heat::Chef',
|
||||
'name': 'config_heat',
|
||||
'config': '...',
|
||||
'inputs': [{'name': 'mode'}],
|
||||
'outputs': [{'name': 'endpoint'}],
|
||||
'options': {}
|
||||
}
|
||||
config = self._create_software_config(**kwargs)
|
||||
config_id = config['id']
|
||||
kwargs = {
|
||||
'config_id': config_id,
|
||||
'input_values': {'mode': 'standalone'},
|
||||
'signal_id': None,
|
||||
'action': 'INIT',
|
||||
'status': 'COMPLETE',
|
||||
'status_reason': ''
|
||||
}
|
||||
deployment = self._create_software_deployment(**kwargs)
|
||||
deployment_id = deployment['id']
|
||||
deployment = self.engine.show_software_deployment(
|
||||
self.ctx, deployment_id)
|
||||
self.assertEqual(deployment_id, deployment['id'])
|
||||
self.assertEqual(config['inputs'], deployment['inputs'])
|
||||
self.assertEqual(kwargs['input_values'], deployment['input_values'])
|
||||
|
||||
def test_update_software_deployment(self):
|
||||
deployment = self._create_software_deployment()
|
||||
self.assertIsNotNone(deployment)
|
||||
deployment_id = deployment['id']
|
||||
deployment_action = deployment['action']
|
||||
self.assertEqual('INIT', deployment_action)
|
||||
config_id = deployment['config_id']
|
||||
self.assertIsNotNone(config_id)
|
||||
updated = self.engine.update_software_deployment(
|
||||
self.ctx, deployment_id=deployment_id, config_id=config_id,
|
||||
input_values={}, output_values={}, action='DEPLOY',
|
||||
status='WAITING', status_reason='')
|
||||
self.assertIsNotNone(updated)
|
||||
self.assertEqual(config_id, updated['config_id'])
|
||||
self.assertEqual('DEPLOY', updated['action'])
|
||||
self.assertEqual('WAITING', updated['status'])
|
||||
|
||||
def check_software_deployment_updated(**kwargs):
|
||||
values = {
|
||||
'config_id': None,
|
||||
'input_values': {},
|
||||
'output_values': {},
|
||||
'action': {},
|
||||
'status': 'WAITING',
|
||||
'status_reason': ''
|
||||
}
|
||||
values.update(kwargs)
|
||||
updated = self.engine.update_software_deployment(
|
||||
self.ctx, deployment_id, **values)
|
||||
for key, value in kwargs.iteritems():
|
||||
self.assertEqual(value, updated[key])
|
||||
|
||||
check_software_deployment_updated(config_id=config_id)
|
||||
check_software_deployment_updated(input_values={'foo': 'fooooo'})
|
||||
check_software_deployment_updated(output_values={'bar': 'baaaaa'})
|
||||
check_software_deployment_updated(action='DEPLOY')
|
||||
check_software_deployment_updated(status='COMPLETE')
|
||||
check_software_deployment_updated(status_reason='Done!')
|
||||
|
||||
def test_delete_software_deployment(self):
|
||||
deployment_id = str(uuid.uuid4())
|
||||
self.assertRaises(exception.NotFound,
|
||||
self.engine.delete_software_deployment,
|
||||
self.ctx, deployment_id)
|
||||
deployment = self._create_software_deployment()
|
||||
self.assertIsNotNone(deployment)
|
||||
deployment_id = deployment['id']
|
||||
deployments = self.engine.list_software_deployments(
|
||||
self.ctx, server_id=None)
|
||||
deployment_ids = [x['id'] for x in deployments]
|
||||
self.assertIn(deployment_id, deployment_ids)
|
||||
self.engine.delete_software_deployment(self.ctx, deployment_id)
|
||||
deployments = self.engine.list_software_deployments(
|
||||
self.ctx, server_id=None)
|
||||
deployment_ids = [x['id'] for x in deployments]
|
||||
self.assertNotIn(deployment_id, deployment_ids)
|
||||
|
|
|
@ -198,3 +198,57 @@ class EngineRpcAPITestCase(testtools.TestCase):
|
|||
def test_set_watch_state(self):
|
||||
self._test_engine_api('set_watch_state', 'call',
|
||||
watch_name='watch1', state="xyz")
|
||||
|
||||
def test_show_software_config(self):
|
||||
self._test_engine_api('show_software_config', 'call',
|
||||
config_id='cda89008-6ea6-4057-b83d-ccde8f0b48c9')
|
||||
|
||||
def test_create_software_config(self):
|
||||
self._test_engine_api('create_software_config', 'call',
|
||||
group='Heat::Shell',
|
||||
name='config_mysql',
|
||||
config='#!/bin/bash',
|
||||
inputs=[],
|
||||
outputs=[],
|
||||
options={})
|
||||
|
||||
def test_delete_software_config(self):
|
||||
self._test_engine_api('delete_software_config', 'call',
|
||||
config_id='cda89008-6ea6-4057-b83d-ccde8f0b48c9')
|
||||
|
||||
def test_list_software_deployments(self):
|
||||
self._test_engine_api('list_software_deployments', 'call',
|
||||
server_id=None)
|
||||
self._test_engine_api('list_software_deployments', 'call',
|
||||
server_id='9dc13236-d342-451f-a885-1c82420ba5ed')
|
||||
|
||||
def test_show_software_deployment(self):
|
||||
deployment_id = '86729f02-4648-44d8-af44-d0ec65b6abc9'
|
||||
self._test_engine_api('show_software_deployment', 'call',
|
||||
deployment_id=deployment_id)
|
||||
|
||||
def test_create_software_deployment(self):
|
||||
self._test_engine_api('create_software_deployment', 'call',
|
||||
server_id='9f1f0e00-05d2-4ca5-8602-95021f19c9d0',
|
||||
config_id='48e8ade1-9196-42d5-89a2-f709fde42632',
|
||||
input_values={},
|
||||
signal_id=None,
|
||||
action='INIT',
|
||||
status='COMPLETE',
|
||||
status_reason=None)
|
||||
|
||||
def test_update_software_deployment(self):
|
||||
deployment_id = '86729f02-4648-44d8-af44-d0ec65b6abc9'
|
||||
self._test_engine_api('update_software_deployment', 'call',
|
||||
deployment_id=deployment_id,
|
||||
config_id='48e8ade1-9196-42d5-89a2-f709fde42632',
|
||||
input_values={},
|
||||
output_values={},
|
||||
action='DEPLOYED',
|
||||
status='COMPLETE',
|
||||
status_reason=None)
|
||||
|
||||
def test_delete_software_deployment(self):
|
||||
deployment_id = '86729f02-4648-44d8-af44-d0ec65b6abc9'
|
||||
self._test_engine_api('delete_software_deployment', 'call',
|
||||
deployment_id=deployment_id)
|
||||
|
|
Loading…
Reference in New Issue