OpenstackClient plugin for stack adopt
This change implements the "openstack stack adopt" command. Blueprint: heat-support-python-openstackclient Change-Id: Id2e74970937c04f095b4f14be047cff6e2bf3891
This commit is contained in:
parent
0a4f2af09c
commit
f225c4e956
@ -20,6 +20,7 @@ from cliff import show
|
||||
from openstackclient.common import exceptions as exc
|
||||
from openstackclient.common import parseractions
|
||||
from openstackclient.common import utils
|
||||
from six.moves.urllib import request
|
||||
|
||||
from heatclient.common import http
|
||||
from heatclient.common import template_utils
|
||||
@ -566,3 +567,88 @@ def _list(client, args=None):
|
||||
columns,
|
||||
(utils.get_item_properties(s, columns) for s in data)
|
||||
)
|
||||
|
||||
|
||||
class AdoptStack(show.ShowOne):
|
||||
"""Adopt a stack."""
|
||||
|
||||
log = logging.getLogger(__name__ + '.AdoptStack')
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(AdoptStack, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'name',
|
||||
metavar='<STACK_NAME>',
|
||||
help=_('Name of the stack to adopt')
|
||||
)
|
||||
parser.add_argument(
|
||||
'-e', '--environment',
|
||||
metavar='<FILE or URL>',
|
||||
action='append',
|
||||
help=_('Path to the environment. Can be specified multiple times')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--timeout',
|
||||
metavar='<TIMEOUT>',
|
||||
type=int,
|
||||
help=_('Stack creation timeout in minutes')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--adopt-file',
|
||||
metavar='<FILE or URL>',
|
||||
required=True,
|
||||
help=_('Path to adopt stack data file')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--enable-rollback',
|
||||
action='store_true',
|
||||
help=_('Enable rollback on create/update failure')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--parameter',
|
||||
metavar='<KEY=VALUE>',
|
||||
action='append',
|
||||
help=_('Parameter values used to create the stack. Can be '
|
||||
'specified multiple times')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--wait',
|
||||
action='store_true',
|
||||
help=_('Wait until stack adopt completes')
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
self.log.debug('take_action(%s)', parsed_args)
|
||||
|
||||
client = self.app.client_manager.orchestration
|
||||
|
||||
env_files, env = (
|
||||
template_utils.process_multiple_environments_and_files(
|
||||
env_paths=parsed_args.environment))
|
||||
|
||||
adopt_url = heat_utils.normalise_file_path_to_url(
|
||||
parsed_args.adopt_file)
|
||||
adopt_data = request.urlopen(adopt_url).read().decode('utf-8')
|
||||
|
||||
fields = {
|
||||
'stack_name': parsed_args.name,
|
||||
'disable_rollback': not parsed_args.enable_rollback,
|
||||
'adopt_stack_data': adopt_data,
|
||||
'parameters': heat_utils.format_parameters(parsed_args.parameter),
|
||||
'files': dict(list(env_files.items())),
|
||||
'environment': env,
|
||||
'timeout': parsed_args.timeout
|
||||
}
|
||||
|
||||
stack = client.stacks.create(**fields)['stack']
|
||||
|
||||
if parsed_args.wait:
|
||||
if not utils.wait_for_status(client.stacks.get, parsed_args.name,
|
||||
status_field='stack_status',
|
||||
success_status='create_complete',
|
||||
error_status=['create_failed']):
|
||||
msg = _('Stack %s failed to create.') % parsed_args.name
|
||||
raise exc.CommandError(msg)
|
||||
|
||||
return _show_stack(client, stack['id'], format='table', short=True)
|
||||
|
32
heatclient/tests/test_templates/adopt.json
Normal file
32
heatclient/tests/test_templates/adopt.json
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
'files': {},
|
||||
'status': 'COMPLETE',
|
||||
'name': 'my_stack',
|
||||
'tags': None,
|
||||
'stack_user_project_id': '123456',
|
||||
'environment': {},
|
||||
'template': {
|
||||
'heat_template_version': '2016-04-08',
|
||||
'resources': {
|
||||
'thing': {
|
||||
'type': 'OS::Heat::TestResource'
|
||||
}
|
||||
}
|
||||
},
|
||||
'action': 'CREATE',
|
||||
'project_id': '56789',
|
||||
'id': '2468',
|
||||
'resources': {
|
||||
'thing': {
|
||||
'status': 'COMPLETE',
|
||||
'name': 'thing',
|
||||
'resource_data': {
|
||||
'value': 'test_string',
|
||||
},
|
||||
'resource_id': 'my_stack-thing-1234',
|
||||
'action': 'CREATE',
|
||||
'type': 'OS::Heat::TestResource',
|
||||
'metadata': {}
|
||||
}
|
||||
}
|
||||
}
|
@ -520,3 +520,68 @@ class TestStackList(TestStack):
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
|
||||
self.assertRaises(exc.CommandError, self.cmd.take_action, parsed_args)
|
||||
|
||||
|
||||
class TestStackAdopt(TestStack):
|
||||
|
||||
adopt_file = 'heatclient/tests/test_templates/adopt.json'
|
||||
|
||||
with open(adopt_file, 'r') as f:
|
||||
adopt_data = f.read()
|
||||
|
||||
defaults = {
|
||||
'stack_name': 'my_stack',
|
||||
'disable_rollback': True,
|
||||
'adopt_stack_data': adopt_data,
|
||||
'parameters': {},
|
||||
'files': {},
|
||||
'environment': {},
|
||||
'timeout': None
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
super(TestStackAdopt, self).setUp()
|
||||
self.cmd = stack.AdoptStack(self.app, None)
|
||||
self.stack_client.create = mock.MagicMock(
|
||||
return_value={'stack': {'id': '1234'}})
|
||||
|
||||
def test_stack_adopt_defaults(self):
|
||||
arglist = ['my_stack', '--adopt-file', self.adopt_file]
|
||||
cols = ['id', 'stack_name', 'description', 'creation_time',
|
||||
'updated_time', 'stack_status', 'stack_status_reason']
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
|
||||
columns, data = self.cmd.take_action(parsed_args)
|
||||
|
||||
self.stack_client.create.assert_called_with(**self.defaults)
|
||||
self.assertEqual(cols, columns)
|
||||
|
||||
def test_stack_adopt_enable_rollback(self):
|
||||
arglist = ['my_stack', '--adopt-file', self.adopt_file,
|
||||
'--enable-rollback']
|
||||
kwargs = copy.deepcopy(self.defaults)
|
||||
kwargs['disable_rollback'] = False
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
self.stack_client.create.assert_called_with(**kwargs)
|
||||
|
||||
def test_stack_adopt_wait(self):
|
||||
arglist = ['my_stack', '--adopt-file', self.adopt_file, '--wait']
|
||||
self.stack_client.get = mock.MagicMock(return_value=(
|
||||
stacks.Stack(None, {'stack_status': 'CREATE_COMPLETE'})))
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
self.stack_client.create.assert_called_with(**self.defaults)
|
||||
self.stack_client.get.assert_called_with(**{'stack_id': '1234'})
|
||||
|
||||
def test_stack_adopt_wait_fail(self):
|
||||
arglist = ['my_stack', '--adopt-file', self.adopt_file, '--wait']
|
||||
self.stack_client.get = mock.MagicMock(return_value=(
|
||||
stacks.Stack(None, {'stack_status': 'CREATE_FAILED'})))
|
||||
parsed_args = self.check_parser(self.cmd, arglist, [])
|
||||
|
||||
self.assertRaises(exc.CommandError, self.cmd.take_action, parsed_args)
|
||||
|
Loading…
Reference in New Issue
Block a user