Remove stack manager class.
StackManager class is obsolete and it has been replaced from tobiko.openstack.heat._stack.HeatStackFixture class. Change-Id: Id2efa0372a4eae942769b7913173b99488bc7359
This commit is contained in:
parent
2e4030d8ad
commit
c261ef1ae5
@ -20,7 +20,6 @@ import argparse
|
||||
|
||||
from oslo_log import log
|
||||
|
||||
from tobiko.common.managers import stack
|
||||
from tobiko.common.managers import ansible
|
||||
from tobiko import config
|
||||
|
||||
@ -38,11 +37,8 @@ class TobikoCMD(object):
|
||||
self.args = (self.parser).parse_args()
|
||||
|
||||
curr_dir = os.path.dirname(__file__)
|
||||
self.templates_dir = os.path.join(curr_dir,
|
||||
"../tests/scenario/templates")
|
||||
self.playbooks_dir = os.path.join(curr_dir,
|
||||
"../tests/scenario/playbooks")
|
||||
self.stackManager = stack.StackManager(self.templates_dir)
|
||||
self.ansibleManager = ansible.AnsibleManager(self.playbooks_dir)
|
||||
|
||||
def get_parser(self):
|
||||
@ -61,7 +57,3 @@ class TobikoCMD(object):
|
||||
for handler in root_logger.handlers:
|
||||
if isinstance(handler, logging.StreamHandler):
|
||||
handler.setLevel(level)
|
||||
self.stackManager = stack.StackManager(
|
||||
templates_dir=self.templates_dir)
|
||||
self.ansibleManager = ansible.AnsibleManager(
|
||||
playbooks_dir=self.playbooks_dir)
|
||||
|
@ -13,14 +13,12 @@
|
||||
# under the License.
|
||||
from __future__ import absolute_import
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
from oslo_log import log
|
||||
|
||||
import tobiko
|
||||
from tobiko.cmd import base
|
||||
from tobiko.common import constants
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
@ -29,37 +27,12 @@ class CreateUtil(base.TobikoCMD):
|
||||
|
||||
def get_parser(self):
|
||||
parser = super(CreateUtil, self).get_parser()
|
||||
parser.add_argument(
|
||||
'--stack', '-s',
|
||||
help="The name of the stack to create.\n"
|
||||
"This is based on the template name in templates dir")
|
||||
parser.add_argument(
|
||||
'--playbook', '-p',
|
||||
help="The name of the playbook to execute.\n"
|
||||
"This is based on the playbook name in playbooks dir")
|
||||
parser.add_argument(
|
||||
'--all', '-a', action='store_true', dest='all',
|
||||
help="Create all the stacks defined in Tobiko.")
|
||||
parser.add_argument(
|
||||
'--wait', '-w', action='store_true', dest='wait',
|
||||
help="Wait for stack to reach CREATE_COMPLETE status before "
|
||||
"exiting.")
|
||||
return parser
|
||||
|
||||
def create_stacks(self, stack_name=None, all_stacks=False, wait=False):
|
||||
"""Creates a stack based on given arguments."""
|
||||
if all_stacks or stack_name is None:
|
||||
templates = self.stackManager.get_templates_names()
|
||||
else:
|
||||
templates = [stack_name + constants.TEMPLATE_SUFFIX]
|
||||
for template in templates:
|
||||
stack_name = os.path.splitext(template)[0]
|
||||
self.stackManager.create_stack(
|
||||
stack_name=stack_name,
|
||||
template_name=template,
|
||||
parameters=constants.DEFAULT_PARAMS,
|
||||
wait=wait)
|
||||
|
||||
def run_playbook(self, playbook):
|
||||
"""Executes given playbook."""
|
||||
self.ansibleManager.run_playbook(playbook, mode='create')
|
||||
@ -75,10 +48,6 @@ def main():
|
||||
create_cmd.set_stream_handler_logging_level()
|
||||
if create_cmd.args.playbook:
|
||||
create_cmd.run_playbook(create_cmd.args.playbook)
|
||||
else:
|
||||
create_cmd.create_stacks(stack_name=create_cmd.args.stack,
|
||||
all_stacks=create_cmd.args.all,
|
||||
wait=create_cmd.args.wait)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -25,31 +25,11 @@ class DeleteUtil(base.TobikoCMD):
|
||||
|
||||
def get_parser(self):
|
||||
parser = super(DeleteUtil, self).get_parser()
|
||||
parser.add_argument(
|
||||
'--stack', '-s',
|
||||
help="The name of the stack to remove.")
|
||||
parser.add_argument(
|
||||
'--all', '-a', action='store_true', dest='all',
|
||||
help="Remove all the stacks created by Tobiko.")
|
||||
parser.add_argument(
|
||||
'--wait', '-w', action='store_true', dest='wait',
|
||||
help="Wait for stack to be deleted before exiting.")
|
||||
parser.add_argument(
|
||||
'--playbook', '-p',
|
||||
help="The name of the playbook to execute in delete mode.")
|
||||
return parser
|
||||
|
||||
def delete_stack(self, stack_name=None, all_stacks=False, wait=False):
|
||||
"""Deletes a stack based on given arguments."""
|
||||
if all_stacks or stack_name is None:
|
||||
stacks = self.stackManager.get_stacks_match_templates()
|
||||
for stack in stacks:
|
||||
self.stackManager.delete_stack(stack, wait=wait)
|
||||
LOG.info("Deleted stack: %s", stack)
|
||||
else:
|
||||
self.stackManager.delete_stack(stack_name, wait=wait)
|
||||
LOG.info("Deleted stack: %s", stack_name)
|
||||
|
||||
def run_playbook(self, playbook):
|
||||
"""Executes given playbook."""
|
||||
self.ansibleManager.run_playbook(playbook, mode='delete')
|
||||
@ -59,12 +39,7 @@ def main():
|
||||
"""Delete CLI main entry."""
|
||||
delete_cmd = DeleteUtil()
|
||||
delete_cmd.set_stream_handler_logging_level()
|
||||
if delete_cmd.args.playbook:
|
||||
delete_cmd.run_playbook(delete_cmd.args.playbook)
|
||||
else:
|
||||
delete_cmd.delete_stack(stack_name=delete_cmd.args.stack,
|
||||
all_stacks=delete_cmd.args.all,
|
||||
wait=delete_cmd.args.wait)
|
||||
delete_cmd.run_playbook(delete_cmd.args.playbook)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -31,30 +31,12 @@ class ListUtil(base.TobikoCMD):
|
||||
|
||||
def get_parser(self):
|
||||
parser = argparse.ArgumentParser(add_help=True)
|
||||
parser.add_argument('--stacks', '-s',
|
||||
help="List stacks (created by Tobiko)",
|
||||
const='list_stacks',
|
||||
action='store_const', dest='action')
|
||||
parser.add_argument('--templates', '-t',
|
||||
help="List templates provided by Tobiko",
|
||||
const='list_templates',
|
||||
action='store_const', dest='action')
|
||||
parser.add_argument('--playbooks', '-p',
|
||||
help="List playbooks provided by Tobiko",
|
||||
const='list_playbooks',
|
||||
action='store_const', dest='action')
|
||||
return parser
|
||||
|
||||
def list_stacks(self):
|
||||
"""Lists stacks created by Tobiko."""
|
||||
for stack in self.stackManager.get_stacks_match_templates():
|
||||
sys.stdout.write(stack + '\n')
|
||||
|
||||
def list_templates(self):
|
||||
"""Lists templates included in Tobiko."""
|
||||
for template in self.stackManager.get_templates_names():
|
||||
sys.stdout.write(template + '\n')
|
||||
|
||||
def list_playbooks(self):
|
||||
"""Lists playbooks included in Tobiko."""
|
||||
for playbook in self.ansibleManager.get_playbooks_names():
|
||||
@ -68,7 +50,7 @@ def main():
|
||||
action_func = getattr(list_cmd, list_cmd.args.action)
|
||||
action_func()
|
||||
else:
|
||||
list_cmd.list_templates()
|
||||
list_cmd.list_playbooks()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -1,35 +0,0 @@
|
||||
# Copyright 2018 Red Hat
|
||||
#
|
||||
# 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 __future__ import absolute_import
|
||||
|
||||
from tobiko.openstack import neutron
|
||||
|
||||
|
||||
class NetworkManager(object):
|
||||
"""Manages Neutron Resources."""
|
||||
|
||||
_client = None
|
||||
|
||||
@property
|
||||
def client(self):
|
||||
if not self._client:
|
||||
self._client = neutron.get_neutron_client()
|
||||
return self._client
|
||||
|
||||
def create_sg_rules(self, rules, sg_id):
|
||||
"""Creates security group rules."""
|
||||
for rule in rules:
|
||||
rule['security_group_id'] = sg_id
|
||||
body = {'security_group_rule': rule}
|
||||
self.client.create_security_group_rule(body)
|
@ -1,207 +0,0 @@
|
||||
# Copyright 2018 Red Hat
|
||||
#
|
||||
# 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 __future__ import absolute_import
|
||||
|
||||
import os
|
||||
import time
|
||||
|
||||
from heatclient.common import template_utils
|
||||
from heatclient import exc
|
||||
from oslo_log import log
|
||||
import yaml
|
||||
|
||||
import tobiko
|
||||
from tobiko.common import constants
|
||||
from tobiko.openstack import heat
|
||||
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
# Status
|
||||
CREATE_IN_PROGRESS = 'CREATE_IN_PROGRESS'
|
||||
CREATE_COMPLETE = 'CREATE_COMPLETE'
|
||||
CREATE_FAILED = 'CREATE_FAILED'
|
||||
DELETE_IN_PROGRESS = 'DELETE_IN_PROGRESS'
|
||||
DELETE_COMPLETE = 'DELETE_COMPLETE'
|
||||
DELETE_FAILED = 'DELETE_FAILED'
|
||||
|
||||
|
||||
class StackManager(object):
|
||||
"""Manages Heat stacks."""
|
||||
|
||||
def __init__(self, templates_dir, wait_interval=5):
|
||||
self.templates_dir = templates_dir
|
||||
self.wait_interval = wait_interval
|
||||
|
||||
_client = None
|
||||
|
||||
@property
|
||||
def client(self):
|
||||
if not self._client:
|
||||
self._client = heat.get_heat_client()
|
||||
return self._client
|
||||
|
||||
def load_template(self, template_path):
|
||||
"""Loads template from a given file."""
|
||||
_, template = template_utils.get_template_contents(template_path)
|
||||
return yaml.safe_dump(template)
|
||||
|
||||
def create_stack(self, stack_name, template_name, parameters, wait=True):
|
||||
"""Creates stack based on passed parameters."""
|
||||
stack = self.wait_for_stack_status(
|
||||
stack_name=stack_name, expected_status={DELETE_COMPLETE,
|
||||
CREATE_COMPLETE,
|
||||
CREATE_FAILED})
|
||||
if stack and stack.stack_status == CREATE_COMPLETE:
|
||||
LOG.debug('Stack %r already exists.', stack_name)
|
||||
return stack
|
||||
|
||||
if stack and stack.stack_status.endswith('_FAILED'):
|
||||
self.delete_stack(stack_name, wait=True)
|
||||
|
||||
template = self.load_template(os.path.join(self.templates_dir,
|
||||
template_name))
|
||||
|
||||
try:
|
||||
self.client.stacks.create(stack_name=stack_name,
|
||||
template=template,
|
||||
parameters=parameters)
|
||||
except exc.HTTPConflict:
|
||||
LOG.debug('Stack %r already exists.', stack_name)
|
||||
else:
|
||||
LOG.debug('Crating stack %r...', stack_name)
|
||||
|
||||
if wait:
|
||||
return self.wait_for_stack_status(stack_name=stack_name)
|
||||
else:
|
||||
return self.get_stack(stack_name=stack_name)
|
||||
|
||||
def delete_stack(self, stack_name, wait=False):
|
||||
"""Deletes stack."""
|
||||
self.client.stacks.delete(stack_name)
|
||||
if wait:
|
||||
self.wait_for_stack_status(stack_name,
|
||||
expected_status={DELETE_COMPLETE})
|
||||
|
||||
def get_stack(self, stack_name, resolve_outputs=False):
|
||||
"""Returns stack ID."""
|
||||
try:
|
||||
return self.client.stacks.get(stack_name,
|
||||
resolve_outputs=resolve_outputs)
|
||||
except exc.HTTPNotFound:
|
||||
return None
|
||||
|
||||
def wait_for_resource_status(self, stack_id, resource_name,
|
||||
status=CREATE_COMPLETE):
|
||||
"""Waits for resource to reach the given status."""
|
||||
res = self.client.resources.get(stack_id, resource_name)
|
||||
while (res.resource_status != status):
|
||||
time.sleep(self.wait_interval)
|
||||
res = self.client.resources.get(stack_id, resource_name)
|
||||
|
||||
def wait_for_stack_status(self, stack_name=None, stack=None,
|
||||
expected_status=None, check=True):
|
||||
"""Waits for the stack to reach the given status."""
|
||||
expected_status = expected_status or {CREATE_COMPLETE}
|
||||
stack_name = stack_name or stack.stack_name
|
||||
stack = stack or self.get_stack(stack_name=stack_name)
|
||||
while (stack and stack.stack_status.endswith('_IN_PROGRESS') and
|
||||
stack.stack_status not in expected_status):
|
||||
LOG.debug("Waiting for %r stack status (observed=%r, expected=%r)",
|
||||
stack_name, stack.stack_status, expected_status)
|
||||
time.sleep(self.wait_interval)
|
||||
stack = self.get_stack(stack_name=stack_name)
|
||||
|
||||
if check:
|
||||
if stack is None:
|
||||
if DELETE_COMPLETE not in expected_status:
|
||||
raise StackNotFound(name=stack_name)
|
||||
else:
|
||||
check_stack_status(stack, expected_status)
|
||||
return stack
|
||||
|
||||
def get_output(self, stack, key):
|
||||
"""Returns a specific value from stack outputs by using a given key."""
|
||||
check_stack_status(stack, {CREATE_COMPLETE})
|
||||
if not hasattr(stack, 'outputs'):
|
||||
stack = self.get_stack(stack_name=stack.stack_name,
|
||||
resolve_outputs=True)
|
||||
outputs = {output['output_key']: output['output_value']
|
||||
for output in stack.outputs}
|
||||
try:
|
||||
return outputs[key]
|
||||
except KeyError:
|
||||
raise InvalidOutputKey(name=stack.stack_name,
|
||||
key=key)
|
||||
|
||||
def get_templates_names(self, strip_suffix=False):
|
||||
"""Returns a list of all the files in templates dir."""
|
||||
templates = []
|
||||
for (_, _, files) in os.walk(self.templates_dir):
|
||||
templates.extend(files)
|
||||
if strip_suffix:
|
||||
templates = [
|
||||
f[:-len(constants.TEMPLATE_SUFFIX)] for f in templates]
|
||||
return templates
|
||||
|
||||
def get_stacks_match_templates(self):
|
||||
"""Returns a list of existing stack names in the cloud project
|
||||
which match the templates defined in the project source code."""
|
||||
matched_stacks = []
|
||||
|
||||
code_stacks = self.get_templates_names(strip_suffix=True)
|
||||
cloud_stacks = self.client.stacks.list()
|
||||
|
||||
for stack in cloud_stacks:
|
||||
if stack.stack_name in code_stacks:
|
||||
matched_stacks.append(stack.stack_name)
|
||||
|
||||
return matched_stacks
|
||||
|
||||
|
||||
def check_stack_status(stack, expected):
|
||||
observed = stack.stack_status
|
||||
if observed not in expected:
|
||||
if observed == CREATE_FAILED:
|
||||
error_class = StackCreationFailed
|
||||
elif observed == DELETE_FAILED:
|
||||
error_class = StackDeletionFailed
|
||||
else:
|
||||
error_class = InvalidStackStatus
|
||||
raise error_class(name=stack.stack_name,
|
||||
observed=observed,
|
||||
expected=expected,
|
||||
reason=stack.stack_status_reason)
|
||||
|
||||
|
||||
class InvalidOutputKey(tobiko.TobikoException):
|
||||
msg = ("Output key %(key)r not found in stack %(name).")
|
||||
|
||||
|
||||
class StackNotFound(tobiko.TobikoException):
|
||||
msg = ("Stack %(name)r not found")
|
||||
|
||||
|
||||
class InvalidStackStatus(tobiko.TobikoException):
|
||||
msg = ("Stack %(name)r status %(observed)r not in %(expected)r "
|
||||
"(reason=%(status_reason)r)")
|
||||
|
||||
|
||||
class StackCreationFailed(InvalidStackStatus):
|
||||
pass
|
||||
|
||||
|
||||
class StackDeletionFailed(InvalidStackStatus):
|
||||
pass
|
@ -25,9 +25,7 @@ class TobikoCMDTest(test_base.OpenstackTest):
|
||||
|
||||
def test_init(self, argv=None):
|
||||
self.patch_argv(argv=argv)
|
||||
cmd = self.command_class()
|
||||
self.assertIsNotNone(cmd.stackManager)
|
||||
return cmd
|
||||
return self.command_class()
|
||||
|
||||
def patch_argv(self, argv=None):
|
||||
return self.patch('sys.argv', [self.command_name] + (argv or []))
|
||||
|
@ -25,7 +25,6 @@ import tobiko
|
||||
from tobiko.openstack import heat
|
||||
from tobiko.openstack import keystone
|
||||
from tobiko.tests.openstack import base
|
||||
from tobiko.common.managers import stack as _stack
|
||||
|
||||
|
||||
class MyStack(heat.HeatStackFixture):
|
||||
@ -309,11 +308,11 @@ class HeatStackFixtureTest(base.OpenstackTest):
|
||||
client.stacks.delete.assert_called_once_with(stack.stack_name)
|
||||
|
||||
def test_get_outputs(self):
|
||||
stack = mock.MagicMock(stack_status=_stack.CREATE_COMPLETE,
|
||||
outputs=[{'output_key': 'key1',
|
||||
'output_value': 'value1'},
|
||||
{'output_key': 'key2',
|
||||
'output_value': 'value2'}])
|
||||
stack = mock_stack(status='CREATE_COMPLETE',
|
||||
outputs=[{'output_key': 'key1',
|
||||
'output_value': 'value1'},
|
||||
{'output_key': 'key2',
|
||||
'output_value': 'value2'}])
|
||||
client = mock.MagicMock(specs=heatclient.Client)
|
||||
client.stacks.get.return_value = stack
|
||||
stack_fixture = MyStack(client=client)
|
||||
@ -326,5 +325,7 @@ class HeatStackFixtureTest(base.OpenstackTest):
|
||||
self.assertEqual('value2', outputs.key2)
|
||||
|
||||
|
||||
def mock_stack(status, stack_id='<stack-id>'):
|
||||
return mock.MagicMock(stack_status=status, id=stack_id)
|
||||
def mock_stack(status, stack_id='<stack-id>', outputs=None):
|
||||
return mock.MagicMock(stack_status=status,
|
||||
id=stack_id,
|
||||
outputs=outputs or [])
|
||||
|
Loading…
x
Reference in New Issue
Block a user