Update client to create archive and use tripleo-common
The client utils will now run a new playbook to ensure that the local archive directory is created early in the deployment process. This change will allow us to build toward a swift-less deployment. All of the client calls, save one, has been moved to use tripleo-common which will assist us to better manage, and migrate from swift storage to a local archive. > As a product of this change all of the "webhook" calls have been removed. which was deprecated as part of the Zaqar and Mistral work. These calls were removed because several swift calls were tied into them, and because mistral is no longer part of the stack, and has been gone for a few cycles, we can safely remove these calls which do nothing. Depends-On: Ibe9b2ffe94cdf493fc84366979d1d78b8528ea1b Change-Id: I7531612a49527f8a21df415c648acb41ac7a0b10 Signed-off-by: Kevin Carter <kecarter@redhat.com>
This commit is contained in:
parent
207951e207
commit
7c40575d7b
|
@ -13,7 +13,6 @@ python-openstackclient>=5.2.0 # Apache-2.0
|
|||
simplejson>=3.5.1 # MIT
|
||||
six>=1.10.0 # MIT
|
||||
osc-lib>=1.8.0 # Apache-2.0
|
||||
websocket-client>=0.44.0 # LGPLv2+
|
||||
tripleo-common>=12.7.0 # Apache-2.0
|
||||
cryptography>=2.1 # BSD/Apache-2.0
|
||||
ansible-runner>=1.4.5 # Apache 2.0
|
||||
|
|
|
@ -25,10 +25,6 @@ class WorkflowServiceError(Base):
|
|||
"""The service type is unknown"""
|
||||
|
||||
|
||||
class WebSocketTimeout(Base):
|
||||
"""Timed out waiting for messages on the websocket"""
|
||||
|
||||
|
||||
class WebSocketConnectionClosed(Base):
|
||||
"""Websocket connection is closed before wait for messages"""
|
||||
|
||||
|
|
|
@ -18,12 +18,12 @@ import json
|
|||
import logging
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import yaml
|
||||
|
||||
from osc_lib.i18n import _
|
||||
|
||||
from tripleo_common import constants as tripleo_common_constants
|
||||
from tripleo_common.utils import swift as swiftutils
|
||||
from tripleoclient import constants
|
||||
from tripleoclient import utils as oooutils
|
||||
|
||||
|
@ -35,13 +35,11 @@ def export_passwords(swift, stack, excludes=True):
|
|||
# Export the passwords from swift
|
||||
obj = 'plan-environment.yaml'
|
||||
container = stack
|
||||
try:
|
||||
resp_headers, content = swift.get_object(container, obj)
|
||||
except Exception as e:
|
||||
LOG.error("An error happened while exporting the password "
|
||||
"file from swift: %s", str(e))
|
||||
sys.exit(1)
|
||||
|
||||
content = swiftutils.get_object_string(
|
||||
swift,
|
||||
container=container,
|
||||
object_name=obj
|
||||
)
|
||||
data = yaml.safe_load(content)
|
||||
# The "passwords" key in plan-environment.yaml are generated passwords,
|
||||
# they are not necessarily the actual password values used during the
|
||||
|
|
|
@ -15,16 +15,10 @@
|
|||
|
||||
"""OpenStackClient Plugin interface"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
import socket
|
||||
import uuid
|
||||
|
||||
from osc_lib import utils
|
||||
from swiftclient import client as swift_client
|
||||
import websocket
|
||||
|
||||
from tripleoclient import exceptions
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
@ -65,117 +59,6 @@ def build_option_parser(parser):
|
|||
return parser
|
||||
|
||||
|
||||
class WebsocketClient(object):
|
||||
|
||||
def __init__(self, instance, queue_name="tripleo", cacert=None):
|
||||
self._project_id = None
|
||||
self._ws = None
|
||||
self._websocket_client_id = None
|
||||
self._queue_name = queue_name
|
||||
|
||||
endpoint = instance.get_endpoint_for_service_type(
|
||||
'messaging-websocket')
|
||||
token = instance.auth.get_token(instance.session)
|
||||
|
||||
self._project_id = instance.auth_ref.project_id
|
||||
|
||||
self._websocket_client_id = str(uuid.uuid4())
|
||||
|
||||
LOG.debug('Instantiating messaging websocket client: %s', endpoint)
|
||||
try:
|
||||
if 'wss:' in endpoint and cacert:
|
||||
OS_CACERT = {"ca_certs": cacert}
|
||||
self._ws = websocket.create_connection(endpoint,
|
||||
sslopt=OS_CACERT)
|
||||
else:
|
||||
self._ws = websocket.create_connection(endpoint)
|
||||
except socket.error:
|
||||
LOG.error("Could not establish a connection to the Zaqar "
|
||||
"websocket. The command was sent but the answer "
|
||||
"could not be read.")
|
||||
raise
|
||||
|
||||
self.send('authenticate', extra_headers={'X-Auth-Token': token})
|
||||
|
||||
# create and subscribe to a queue
|
||||
# NOTE: if the queue exists it will 204
|
||||
self.send('queue_create', {'queue_name': queue_name})
|
||||
self.send('subscription_create', {
|
||||
'queue_name': queue_name,
|
||||
'ttl': 10000
|
||||
})
|
||||
|
||||
def cleanup(self):
|
||||
self._ws.close()
|
||||
|
||||
def send(self, action, body=None, extra_headers=None):
|
||||
|
||||
headers = {
|
||||
'Client-ID': self._websocket_client_id,
|
||||
'X-Project-ID': self._project_id
|
||||
}
|
||||
|
||||
if extra_headers is not None:
|
||||
headers.update(extra_headers)
|
||||
|
||||
msg = {'action': action, 'headers': headers}
|
||||
if body:
|
||||
msg['body'] = body
|
||||
self._ws.send(json.dumps(msg))
|
||||
data = self.recv()
|
||||
if data['headers']['status'] not in (200, 201, 204):
|
||||
raise RuntimeError(data)
|
||||
return data
|
||||
|
||||
def recv(self):
|
||||
content = self._ws.recv()
|
||||
try:
|
||||
return json.loads(content)
|
||||
except ValueError:
|
||||
raise ValueError(
|
||||
"No JSON object could be decoded; {0!r}".format(content))
|
||||
|
||||
def wait_for_messages(self, timeout=None):
|
||||
"""Wait for messages on a Zaqar queue
|
||||
|
||||
A timeout can be provided in seconds, if no timeout is provided it
|
||||
will block forever until a message is received. If no message is
|
||||
received (for example, Zaqar is down) then it will block until manually
|
||||
killed.
|
||||
|
||||
If no timeout is provided this method will never stop waiting for new
|
||||
messages. It is the responsibility of the consumer to stop consuming
|
||||
messages.
|
||||
"""
|
||||
|
||||
if timeout is None:
|
||||
LOG.warning("Waiting for messages on queue '{}' with no timeout."
|
||||
.format(self._queue_name))
|
||||
|
||||
try:
|
||||
self._ws.settimeout(timeout)
|
||||
while True:
|
||||
message = self.recv()
|
||||
LOG.debug(message)
|
||||
yield message['body']['payload']
|
||||
|
||||
except websocket.WebSocketTimeoutException:
|
||||
raise exceptions.WebSocketTimeout()
|
||||
except websocket.WebSocketConnectionClosedException:
|
||||
raise exceptions.WebSocketConnectionClosed()
|
||||
|
||||
def __enter__(self):
|
||||
"""Return self to allow usage as a context manager"""
|
||||
return self
|
||||
|
||||
def __exit__(self, *exc):
|
||||
"""Call cleanup when exiting the context manager"""
|
||||
try:
|
||||
self.cleanup()
|
||||
except websocket.WebSocketConnectionClosedException:
|
||||
pass
|
||||
|
||||
|
||||
class MistralContext(object):
|
||||
"""MistralContext, a shim for calling Mistral actions directly
|
||||
|
||||
|
@ -283,11 +166,6 @@ class ClientWrapper(object):
|
|||
self._local_orchestration = client
|
||||
return self._local_orchestration
|
||||
|
||||
def messaging_websocket(self, queue_name='tripleo'):
|
||||
"""Returns a websocket for the messaging service"""
|
||||
return WebsocketClient(self._instance, queue_name,
|
||||
cacert=self._instance.cacert)
|
||||
|
||||
@property
|
||||
def object_store(self):
|
||||
"""Returns an object_store service client
|
||||
|
|
|
@ -88,21 +88,6 @@ class FakeClientWrapper(object):
|
|||
def __init__(self):
|
||||
self._instance = mock.Mock()
|
||||
self.object_store = FakeObjectClient()
|
||||
self._mock_websocket = mock.Mock()
|
||||
self._mock_websocket.__enter__ = mock.Mock(
|
||||
return_value=self._mock_websocket)
|
||||
# Return False to avoid silencing exceptions
|
||||
self._mock_websocket.__exit__ = mock.Mock(return_value=False)
|
||||
self._mock_websocket.wait_for_messages = mock.Mock(
|
||||
return_value=iter([{
|
||||
"status": "SUCCESS",
|
||||
"message": "Success",
|
||||
"execution_id": "IDID"
|
||||
}])
|
||||
)
|
||||
|
||||
def messaging_websocket(self):
|
||||
return self._mock_websocket
|
||||
|
||||
|
||||
class FakeRunnerConfig(object):
|
||||
|
|
|
@ -36,9 +36,6 @@ class PlanManagementFixture(fixtures.Fixture):
|
|||
|
||||
def _setUp(self):
|
||||
super(PlanManagementFixture, self)._setUp()
|
||||
self.mock_tarball = self.useFixture(fixtures.MockPatch(
|
||||
'tripleoclient.workflows.plan_management.tarball')
|
||||
).mock
|
||||
|
||||
|
||||
class UtilsOvercloudFixture(fixtures.Fixture):
|
||||
|
|
|
@ -12,9 +12,7 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import json
|
||||
import mock
|
||||
import socket
|
||||
|
||||
from tripleoclient import plugin
|
||||
from tripleoclient.tests import base
|
||||
|
@ -23,138 +21,16 @@ from tripleoclient.tests import fakes
|
|||
|
||||
class TestPlugin(base.TestCase):
|
||||
|
||||
@mock.patch("websocket.create_connection")
|
||||
def test_make_client(self, ws_create_connection):
|
||||
def test_make_client(self):
|
||||
clientmgr = mock.MagicMock()
|
||||
clientmgr.get_endpoint_for_service_type.return_value = fakes.WS_URL
|
||||
|
||||
clientmgr.auth.get_token.return_value = "TOKEN"
|
||||
clientmgr.auth_ref.project_id = "ID"
|
||||
clientmgr.cacert = None
|
||||
ws_create_connection.return_value.recv.return_value = json.dumps({
|
||||
"headers": {
|
||||
"status": 200
|
||||
}
|
||||
})
|
||||
client = plugin.make_client(clientmgr)
|
||||
|
||||
websocket = client.messaging_websocket()
|
||||
# The second access should not return the same client:
|
||||
self.assertIsNot(client.messaging_websocket(), websocket)
|
||||
|
||||
plugin.make_client(clientmgr)
|
||||
|
||||
# And the functions should only be called when the client is created:
|
||||
self.assertEqual(clientmgr.auth.get_token.call_count, 2)
|
||||
self.assertEqual(clientmgr.get_endpoint_for_service_type.call_count, 2)
|
||||
ws_create_connection.assert_called_with("ws://0.0.0.0")
|
||||
|
||||
@mock.patch("websocket.create_connection")
|
||||
def test_invalid_json(self, ws_create_connection):
|
||||
clientmgr = mock.MagicMock()
|
||||
clientmgr.get_endpoint_for_service_type.return_value = fakes.WS_URL
|
||||
|
||||
clientmgr.auth.get_token.return_value = "TOKEN"
|
||||
clientmgr.auth_ref.project_id = "ID"
|
||||
clientmgr.cacert = None
|
||||
ws_create_connection.return_value.recv.return_value = "BAD JSON"
|
||||
client = plugin.make_client(clientmgr)
|
||||
|
||||
# NOTE(d0ugal): Manually catching errors rather than using assertRaises
|
||||
# so we can assert the message.
|
||||
try:
|
||||
client.messaging_websocket()
|
||||
self.assertFail()
|
||||
except ValueError as e:
|
||||
self.assertEqual(
|
||||
str(e), "No JSON object could be decoded; 'BAD JSON'")
|
||||
|
||||
@mock.patch.object(plugin.WebsocketClient, "recv")
|
||||
@mock.patch("websocket.create_connection")
|
||||
def test_handle_websocket_multiple(self, ws_create_connection, recv_mock):
|
||||
|
||||
send_ack = {
|
||||
"headers": {
|
||||
"status": 200
|
||||
}
|
||||
}
|
||||
|
||||
# Creating the websocket sends three messages and closing sends one.
|
||||
# The one being tested is wrapped between these
|
||||
recv_mock.side_effect = [send_ack, send_ack, send_ack, {
|
||||
"body": {
|
||||
"payload": {
|
||||
"status": 200,
|
||||
"message": "Result for IDID",
|
||||
"execution_id": "IDID",
|
||||
}
|
||||
}
|
||||
}, send_ack]
|
||||
|
||||
clientmgr = mock.MagicMock()
|
||||
clientmgr.get_endpoint_for_service_type.return_value = fakes.WS_URL
|
||||
clientmgr.auth.get_token.return_value = "TOKEN"
|
||||
clientmgr.auth_ref.project_id = "ID"
|
||||
clientmgr.cacert = None
|
||||
|
||||
client = plugin.make_client(clientmgr)
|
||||
|
||||
with client.messaging_websocket() as ws:
|
||||
for payload in ws.wait_for_messages():
|
||||
self.assertEqual(payload, {
|
||||
"status": 200,
|
||||
"message": "Result for IDID",
|
||||
"execution_id": "IDID",
|
||||
})
|
||||
# We only want to test the first message, as there is only one.
|
||||
# The last one, the send_ack will be used by the context
|
||||
# manager when it is closed.
|
||||
break
|
||||
|
||||
@mock.patch("websocket.create_connection")
|
||||
def test_websocket_creation_error(self, ws_create_connection):
|
||||
|
||||
ws_create_connection.side_effect = socket.error
|
||||
|
||||
clientmgr = mock.MagicMock()
|
||||
clientmgr.get_endpoint_for_service_type.return_value = fakes.WS_URL
|
||||
clientmgr.auth.get_token.return_value = "TOKEN"
|
||||
clientmgr.auth_ref.project_id = "ID"
|
||||
clientmgr.cacert = None
|
||||
|
||||
client = plugin.make_client(clientmgr)
|
||||
|
||||
msg = ("Could not establish a connection to the Zaqar websocket. The "
|
||||
"command was sent but the answer could not be read.")
|
||||
with mock.patch('tripleoclient.plugin.LOG') as mock_log:
|
||||
self.assertRaises(socket.error, client.messaging_websocket)
|
||||
mock_log.error.assert_called_once_with(msg)
|
||||
|
||||
@mock.patch("websocket.create_connection")
|
||||
def test_make_tls_client(self, ws_create_connection):
|
||||
clientmgr = mock.MagicMock()
|
||||
clientmgr.get_endpoint_for_service_type.return_value = fakes.WSS_URL
|
||||
|
||||
clientmgr.auth.get_token.return_value = "TOKEN"
|
||||
clientmgr.auth_ref.project_id = "ID"
|
||||
clientmgr.cacert = '/etc/pki/ca-trust/source/anchors/cm-local-ca.pem'
|
||||
ws_create_connection.return_value.recv.return_value = json.dumps({
|
||||
"headers": {
|
||||
"status": 200
|
||||
}
|
||||
})
|
||||
client = plugin.make_client(clientmgr)
|
||||
|
||||
websocket = client.messaging_websocket()
|
||||
# The second access should not return the same client:
|
||||
self.assertIsNot(client.messaging_websocket(), websocket)
|
||||
|
||||
plugin.make_client(clientmgr)
|
||||
|
||||
# And the functions should only be called when the client is created:
|
||||
self.assertEqual(clientmgr.auth.get_token.call_count, 2)
|
||||
self.assertEqual(clientmgr.get_endpoint_for_service_type.call_count, 2)
|
||||
ws_create_connection.assert_called_with(
|
||||
"wss://0.0.0.0",
|
||||
sslopt={'ca_certs':
|
||||
'/etc/pki/ca-trust/source/anchors/cm-local-ca.pem'})
|
||||
self.assertEqual(clientmgr.auth.get_token.call_count, 0)
|
||||
self.assertEqual(clientmgr.get_endpoint_for_service_type.call_count, 0)
|
||||
|
|
|
@ -48,14 +48,6 @@ class ClientWrapper(object):
|
|||
|
||||
def __init__(self):
|
||||
self._instance = None
|
||||
self._mock_websocket = mock.Mock()
|
||||
self._mock_websocket.__enter__ = mock.Mock(
|
||||
return_value=self._mock_websocket)
|
||||
# Return False to avoid silencing exceptions
|
||||
self._mock_websocket.__exit__ = mock.Mock(return_value=False)
|
||||
|
||||
def messaging_websocket(self):
|
||||
return self._mock_websocket
|
||||
|
||||
|
||||
class TestBaremetal(utils.TestCommand):
|
||||
|
|
|
@ -25,7 +25,6 @@ import mock
|
|||
import openstack
|
||||
from osc_lib import exceptions as oscexc
|
||||
from osc_lib.tests import utils
|
||||
from swiftclient.exceptions import ClientException as ObjectClientException
|
||||
|
||||
from tripleoclient import constants
|
||||
from tripleoclient import exceptions
|
||||
|
@ -102,8 +101,6 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||
client = self.app.client_manager.tripleoclient = plugin.ClientWrapper(
|
||||
instance=ooofakes.FakeInstanceData
|
||||
)
|
||||
client.messaging_websocket = \
|
||||
ooofakes.FakeClientWrapper().messaging_websocket
|
||||
get_object = client.object_store.get_object = mock.Mock()
|
||||
get_object.return_value = ('f1', 'content')
|
||||
client.object_store.put_object = mock.Mock()
|
||||
|
@ -165,11 +162,18 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||
|
||||
client.object_store.get_account = mock.MagicMock()
|
||||
|
||||
self.mock_tar = mock.patch(
|
||||
'tripleo_common.utils.tarball.create_tarball',
|
||||
autospec=True
|
||||
)
|
||||
self.mock_tar.start()
|
||||
|
||||
def tearDown(self):
|
||||
super(TestDeployOvercloud, self).tearDown()
|
||||
os.unlink(self.parameter_defaults_env_file)
|
||||
self.cmd._download_missing_files_from_plan = self.real_download_missing
|
||||
shutil.rmtree = self.real_shutil
|
||||
self.mock_tar.stop()
|
||||
|
||||
@mock.patch('tripleoclient.utils.check_nic_config_with_ansible')
|
||||
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
|
||||
|
@ -368,12 +372,6 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||
|
||||
mock_validate_args.assert_called_once_with(parsed_args)
|
||||
|
||||
plane_management_fixture.mock_tarball.create_tarball.\
|
||||
assert_called_with(
|
||||
self.tmp_dir.join('tripleo-heat-templates'), mock.ANY)
|
||||
plane_management_fixture.mock_tarball.\
|
||||
tarball_extract_to_swift_container.assert_called_with(
|
||||
clients.tripleoclient.object_store, mock.ANY, 'overcloud')
|
||||
self.assertFalse(mock_invoke_plan_env_wf.called)
|
||||
|
||||
calls = [
|
||||
|
@ -393,146 +391,6 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||
object_client = clients.tripleoclient.object_store
|
||||
object_client.put_object.assert_has_calls(calls)
|
||||
|
||||
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
|
||||
'_get_ctlplane_attrs', autospec=True, return_value={})
|
||||
@mock.patch('os.chdir')
|
||||
@mock.patch('tripleoclient.utils.copy_clouds_yaml')
|
||||
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
|
||||
'_get_undercloud_host_entry', autospec=True,
|
||||
return_value='192.168.0.1 uc.ctlplane.localhost uc.ctlplane')
|
||||
@mock.patch('tripleoclient.utils.check_stack_network_matches_env_files')
|
||||
@mock.patch('tripleoclient.utils.check_ceph_fsid_matches_env_files')
|
||||
@mock.patch('shutil.rmtree', autospec=True)
|
||||
@mock.patch('tripleoclient.utils.get_overcloud_endpoint', autospec=True)
|
||||
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
|
||||
'_deploy_postconfig', autospec=True)
|
||||
@mock.patch('tripleo_common.update.add_breakpoints_cleanup_into_env',
|
||||
autospec=True)
|
||||
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
|
||||
'_validate_args')
|
||||
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
|
||||
'_create_parameters_env', autospec=True)
|
||||
@mock.patch('tripleoclient.utils.create_tempest_deployer_input',
|
||||
autospec=True)
|
||||
@mock.patch('tripleoclient.workflows.deployment.create_overcloudrc',
|
||||
autospec=True)
|
||||
@mock.patch('heatclient.common.template_utils.get_template_contents',
|
||||
autospec=True)
|
||||
@mock.patch('tempfile.mkdtemp', autospec=True)
|
||||
def test_tht_deploy_with_plan_environment_file(
|
||||
self, mock_tmpdir, mock_get_template_contents,
|
||||
mock_overcloudrc,
|
||||
mock_create_tempest_deployer, mock_create_parameters_env,
|
||||
mock_validate_args,
|
||||
mock_breakpoints_cleanup,
|
||||
mock_postconfig, mock_shutil_rmtree,
|
||||
mock_invoke_plan_env_wf,
|
||||
mock_stack_network_check, mock_ceph_fsid,
|
||||
mock_get_undercloud_host_entry, mock_copy, mock_chdir,
|
||||
mock_get_ctlplane_attrs):
|
||||
fixture = deployment.DeploymentWorkflowFixture()
|
||||
self.useFixture(fixture)
|
||||
plane_management_fixture = deployment.PlanManagementFixture()
|
||||
self.useFixture(plane_management_fixture)
|
||||
utils_fixture = deployment.UtilsFixture()
|
||||
self.useFixture(utils_fixture)
|
||||
|
||||
arglist = ['--templates', '-p', 'the-plan-environment.yaml']
|
||||
verifylist = [
|
||||
('templates', '/usr/share/openstack-tripleo-heat-templates/'),
|
||||
('plan_environment_file', 'the-plan-environment.yaml')
|
||||
]
|
||||
|
||||
mock_tmpdir.return_value = "/tmp/tht"
|
||||
|
||||
clients = self.app.client_manager
|
||||
orchestration_client = clients.orchestration
|
||||
mock_stack = fakes.create_tht_stack()
|
||||
orchestration_client.stacks.get.side_effect = [None, mock.Mock()]
|
||||
|
||||
def _orch_clt_create(**kwargs):
|
||||
orchestration_client.stacks.get.return_value = mock_stack
|
||||
|
||||
orchestration_client.stacks.create.side_effect = _orch_clt_create
|
||||
|
||||
object_client = clients.tripleoclient.object_store
|
||||
object_client.get_object = mock.Mock()
|
||||
object_client.put_container = mock.Mock()
|
||||
mock_env = yaml.safe_dump({'environments': []})
|
||||
object_client.get_object.return_value = ({}, mock_env)
|
||||
|
||||
clients.network.api.find_attr.return_value = {
|
||||
"id": "network id"
|
||||
}
|
||||
mock_get_template_contents.return_value = [{}, "template"]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
baremetal = clients.baremetal
|
||||
baremetal.node.list.return_value = range(10)
|
||||
|
||||
expected_parameters = {
|
||||
'CephClusterFSID': self.uuid1_value,
|
||||
'CephStorageCount': 3,
|
||||
'ExtraConfig': '{}',
|
||||
'HypervisorNeutronPhysicalBridge': 'br-ex',
|
||||
'HypervisorNeutronPublicInterface': 'nic1',
|
||||
'NeutronDnsmasqOptions': 'dhcp-option-force=26,1400',
|
||||
'NeutronFlatNetworks': 'datacentre',
|
||||
'NeutronNetworkType': 'gre',
|
||||
'NeutronPublicInterface': 'nic1',
|
||||
'NeutronTunnelTypes': 'gre',
|
||||
'NtpServer': '',
|
||||
'SnmpdReadonlyUserPassword': 'PASSWORD',
|
||||
'StackAction': 'CREATE',
|
||||
'DeployIdentifier': 12345678,
|
||||
'RootStackName': 'overcloud',
|
||||
'UndercloudHostsEntries': [
|
||||
'192.168.0.1 uc.ctlplane.localhost uc.ctlplane'
|
||||
],
|
||||
'CtlplaneNetworkAttributes': {},
|
||||
}
|
||||
|
||||
testcase = self
|
||||
|
||||
def _custom_create_params_env(_self, parameters, tht_root,
|
||||
container_name):
|
||||
for key, value in six.iteritems(parameters):
|
||||
testcase.assertEqual(value, expected_parameters[key])
|
||||
parameter_defaults = {"parameter_defaults": parameters}
|
||||
return parameter_defaults
|
||||
|
||||
mock_create_parameters_env.side_effect = _custom_create_params_env
|
||||
|
||||
mock_open_context = mock.mock_open()
|
||||
|
||||
with mock.patch('six.moves.builtins.open', mock_open_context):
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
self.assertFalse(orchestration_client.stacks.create.called)
|
||||
|
||||
mock_get_template_contents.assert_called_with(
|
||||
object_request=mock.ANY,
|
||||
template_object=constants.OVERCLOUD_YAML_NAME)
|
||||
|
||||
mock_create_tempest_deployer.assert_called_with()
|
||||
mock_validate_args.assert_called_once_with(parsed_args)
|
||||
|
||||
plane_management_fixture.mock_tarball.create_tarball.\
|
||||
assert_called_with(
|
||||
'/tmp/tht/tripleo-heat-templates', mock.ANY)
|
||||
plane_management_fixture.mock_tarball.\
|
||||
tarball_extract_to_swift_container.assert_called_with(
|
||||
clients.tripleoclient.object_store, mock.ANY, 'overcloud')
|
||||
|
||||
mock_open_context.assert_has_calls(
|
||||
[mock.call('the-plan-environment.yaml')])
|
||||
clients.tripleoclient.object_store.put_object.assert_called()
|
||||
self.assertTrue(mock_invoke_plan_env_wf.called)
|
||||
mock_copy.assert_called_once()
|
||||
object_client.put_container.assert_called_once_with(
|
||||
'overcloud', headers={'x-container-meta-usage-tripleo': 'plan'})
|
||||
|
||||
@mock.patch('tripleoclient.workflows.deployment.create_overcloudrc',
|
||||
autospec=True)
|
||||
@mock.patch('os.chdir')
|
||||
|
@ -885,8 +743,6 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||
object_client.put_container.assert_called_once_with(
|
||||
'overcloud', headers={'x-container-meta-usage-tripleo': 'plan'})
|
||||
|
||||
@mock.patch('tripleoclient.workflows.plan_management.tarball',
|
||||
autospec=True)
|
||||
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
|
||||
'_deploy_postconfig', autospec=True)
|
||||
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
|
||||
|
@ -895,8 +751,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||
'_heat_deploy', autospec=True)
|
||||
def test_environment_dirs_env_dir_not_found(self, mock_deploy_heat,
|
||||
mock_update_parameters,
|
||||
mock_post_config,
|
||||
mock_tarball):
|
||||
mock_post_config):
|
||||
utils_fixture = deployment.UtilsOvercloudFixture()
|
||||
self.useFixture(utils_fixture)
|
||||
|
||||
|
@ -966,7 +821,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||
'_heat_deploy', autospec=True)
|
||||
def test_try_overcloud_deploy_with_no_templates_existing(
|
||||
self, mock_heat_deploy_func):
|
||||
mock_heat_deploy_func.side_effect = ObjectClientException('error')
|
||||
mock_heat_deploy_func.side_effect = Exception('error')
|
||||
self.assertRaises(ValueError,
|
||||
self.cmd._try_overcloud_deploy_with_compat_yaml,
|
||||
'/fake/path', mock.ANY, mock.ANY, mock.ANY,
|
||||
|
@ -978,7 +833,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
|||
def test_try_overcloud_deploy_show_missing_file(
|
||||
self, mock_heat_deploy_func):
|
||||
mock_heat_deploy_func.side_effect = \
|
||||
ObjectClientException('/fake/path not found')
|
||||
Exception('/fake/path not found')
|
||||
try:
|
||||
self.cmd._try_overcloud_deploy_with_compat_yaml(
|
||||
'/fake/path', mock.ANY, mock.ANY, mock.ANY,
|
||||
|
|
|
@ -31,14 +31,7 @@ class Base(fakes.TestBaremetal):
|
|||
{"name": "hyperthreading", "value": "on"}
|
||||
]
|
||||
}
|
||||
tripleoclient = self.app.client_manager.tripleoclient
|
||||
self.app.client_manager.baremetal.node.list.return_value = []
|
||||
self.websocket = tripleoclient.messaging_websocket()
|
||||
self.websocket.wait_for_messages.return_value = iter([{
|
||||
'status': "SUCCESS",
|
||||
'execution_id': 'fake id',
|
||||
'root_execution_id': 'fake id',
|
||||
}])
|
||||
|
||||
self.execution = self.workflow.executions.create.return_value
|
||||
self.execution.id = 'fake id'
|
||||
|
|
|
@ -105,6 +105,15 @@ class TestOvercloudCreatePlan(utils.TestCommand):
|
|||
|
||||
self.swift = self.app.client_manager.tripleoclient.object_store
|
||||
self.swift.get_account = mock.MagicMock()
|
||||
self.mock_tar = mock.patch(
|
||||
'tripleo_common.utils.tarball.create_tarball',
|
||||
autospec=True
|
||||
)
|
||||
self.mock_tar.start()
|
||||
|
||||
def tearDown(self):
|
||||
super(TestOvercloudCreatePlan, self).tearDown()
|
||||
self.mock_tar.stop()
|
||||
|
||||
@mock.patch("tripleoclient.utils.run_ansible_playbook", autospec=True)
|
||||
@mock.patch('os.chdir', autospec=True)
|
||||
|
@ -123,7 +132,7 @@ class TestOvercloudCreatePlan(utils.TestCommand):
|
|||
self.cmd.take_action(parsed_args)
|
||||
|
||||
# Verify
|
||||
mock_run_playbook.assert_called_once_with(
|
||||
mock_run_playbook.assert_called_with(
|
||||
'cli-create-deployment-plan.yaml',
|
||||
'undercloud,',
|
||||
mock.ANY,
|
||||
|
@ -138,10 +147,9 @@ class TestOvercloudCreatePlan(utils.TestCommand):
|
|||
)
|
||||
|
||||
@mock.patch("tripleoclient.utils.run_ansible_playbook", autospec=True)
|
||||
@mock.patch("tripleoclient.workflows.plan_management.tarball")
|
||||
@mock.patch('os.chdir', autospec=True)
|
||||
@mock.patch('tempfile.mkdtemp', autospec=True)
|
||||
def test_create_custom_plan(self, mock_tmp, mock_cd, mock_tarball,
|
||||
def test_create_custom_plan(self, mock_tmp, mock_cd,
|
||||
mock_run_playbook):
|
||||
|
||||
# Setup
|
||||
|
@ -156,7 +164,7 @@ class TestOvercloudCreatePlan(utils.TestCommand):
|
|||
self.cmd.take_action(parsed_args)
|
||||
|
||||
# Verify
|
||||
mock_run_playbook.assert_called_once_with(
|
||||
mock_run_playbook.assert_called_with(
|
||||
'cli-create-deployment-plan.yaml',
|
||||
'undercloud,',
|
||||
mock.ANY,
|
||||
|
@ -172,11 +180,10 @@ class TestOvercloudCreatePlan(utils.TestCommand):
|
|||
self.swift.get_account.assert_called_once()
|
||||
|
||||
@mock.patch("tripleoclient.utils.run_ansible_playbook", autospec=True)
|
||||
@mock.patch("tripleoclient.workflows.plan_management.tarball")
|
||||
@mock.patch('os.chdir', autospec=True)
|
||||
@mock.patch('tempfile.mkdtemp', autospec=True)
|
||||
def test_create_custom_plan_plan_environment_file(
|
||||
self, mock_tmp, mock_cd, mock_tarball, mock_run_playbook):
|
||||
self, mock_tmp, mock_cd, mock_run_playbook):
|
||||
# Setup
|
||||
arglist = ['overcast', '--templates', '/fake/path',
|
||||
'-p', 'the_plan_environment.yaml']
|
||||
|
@ -197,7 +204,7 @@ class TestOvercloudCreatePlan(utils.TestCommand):
|
|||
[mock.call('the_plan_environment.yaml', 'rb')])
|
||||
|
||||
# Verify
|
||||
mock_run_playbook.assert_called_once_with(
|
||||
mock_run_playbook.assert_called_with(
|
||||
'cli-create-deployment-plan.yaml',
|
||||
'undercloud,',
|
||||
mock.ANY,
|
||||
|
@ -234,7 +241,7 @@ class TestOvercloudCreatePlan(utils.TestCommand):
|
|||
self.app.options = fakes.FakeOptions()
|
||||
self.cmd.take_action(parsed_args)
|
||||
# Verify
|
||||
mock_run_playbook.assert_called_once_with(
|
||||
mock_run_playbook.assert_called_with(
|
||||
'cli-create-deployment-plan.yaml',
|
||||
'undercloud,',
|
||||
mock.ANY,
|
||||
|
@ -267,7 +274,7 @@ class TestOvercloudCreatePlan(utils.TestCommand):
|
|||
self.cmd.take_action(parsed_args)
|
||||
|
||||
# Verify
|
||||
mock_run_playbook.assert_called_once_with(
|
||||
mock_run_playbook.assert_called_with(
|
||||
'cli-create-deployment-plan.yaml',
|
||||
'undercloud,',
|
||||
mock.ANY,
|
||||
|
@ -325,7 +332,7 @@ class TestOvercloudDeployPlan(utils.TestCommand):
|
|||
# Run
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
mock_run_playbook.assert_called_once_with(
|
||||
mock_run_playbook.assert_called_with(
|
||||
'cli-deploy-deployment-plan.yaml',
|
||||
'undercloud,',
|
||||
mock.ANY,
|
||||
|
|
|
@ -37,14 +37,6 @@ class TestCreateRAID(fakes.TestBaremetal):
|
|||
{"foo2": "bar2"}
|
||||
]
|
||||
}
|
||||
tripleoclient = self.app.client_manager.tripleoclient
|
||||
websocket = tripleoclient.messaging_websocket()
|
||||
websocket.wait_for_messages.return_value = iter([
|
||||
{'status': "SUCCESS",
|
||||
'execution_id': 'IDID'}
|
||||
])
|
||||
self.websocket = websocket
|
||||
|
||||
execution = mock.MagicMock(
|
||||
output=json.dumps({
|
||||
"result": None
|
||||
|
|
|
@ -268,13 +268,6 @@ class TestIntrospectNode(fakes.TestOvercloudNode):
|
|||
self.cmd, argslist, verifylist)
|
||||
|
||||
def _check_introspect_all_manageable(self, parsed_args, provide=False):
|
||||
self.websocket.wait_for_messages.return_value = iter([{
|
||||
"status": "SUCCESS",
|
||||
"message": "Success",
|
||||
"introspected_nodes": {},
|
||||
"execution_id": "IDID"
|
||||
}] * 2)
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
call_list = [mock.call(
|
||||
|
@ -293,12 +286,6 @@ class TestIntrospectNode(fakes.TestOvercloudNode):
|
|||
2 if provide else 1)
|
||||
|
||||
def _check_introspect_nodes(self, parsed_args, nodes, provide=False):
|
||||
self.websocket.wait_for_messages.return_value = [{
|
||||
"status": "SUCCESS",
|
||||
"message": "Success",
|
||||
"execution_id": "IDID",
|
||||
}]
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
call_list = [mock.call(
|
||||
|
|
|
@ -1,183 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# 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 json
|
||||
import mock
|
||||
|
||||
from osc_lib.tests import utils
|
||||
|
||||
from tripleoclient import exceptions as ex
|
||||
from tripleoclient.workflows import base
|
||||
|
||||
|
||||
class TestBaseWorkflows(utils.TestCommand):
|
||||
|
||||
def test_wait_for_messages_success(self):
|
||||
payload_a = {
|
||||
'status': 'ERROR',
|
||||
'execution_id': 2,
|
||||
'root_execution_id': 1
|
||||
}
|
||||
payload_b = {
|
||||
'status': 'ERROR',
|
||||
'execution_id': 1,
|
||||
'root_execution_id': 1
|
||||
}
|
||||
|
||||
mistral = mock.Mock()
|
||||
websocket = mock.Mock()
|
||||
websocket.wait_for_messages.return_value = iter([payload_a, payload_b])
|
||||
execution = mock.Mock()
|
||||
execution.id = 1
|
||||
|
||||
messages = list(base.wait_for_messages(mistral, websocket, execution))
|
||||
|
||||
self.assertEqual([payload_a, payload_b], messages)
|
||||
|
||||
self.assertFalse(mistral.executions.get.called)
|
||||
websocket.wait_for_messages.assert_called_with(timeout=None)
|
||||
|
||||
def test_wait_for_messages_timeout(self):
|
||||
mistral = mock.Mock()
|
||||
websocket = mock.Mock()
|
||||
websocket.wait_for_messages.side_effect = ex.WebSocketTimeout
|
||||
execution = mock.Mock()
|
||||
execution.id = 1
|
||||
|
||||
messages = base.wait_for_messages(mistral, websocket, execution)
|
||||
|
||||
self.assertRaises(ex.WebSocketTimeout, list, messages)
|
||||
|
||||
self.assertTrue(mistral.executions.get.called)
|
||||
websocket.wait_for_messages.assert_called_with(timeout=None)
|
||||
|
||||
def test_wait_for_messages_connection_closed(self):
|
||||
mistral = mock.Mock()
|
||||
websocket = mock.Mock()
|
||||
websocket.wait_for_messages.side_effect = ex.WebSocketConnectionClosed
|
||||
|
||||
execution = mock.Mock()
|
||||
execution.id = 1
|
||||
|
||||
messages = base.wait_for_messages(mistral, websocket, execution)
|
||||
|
||||
self.assertRaises(ex.WebSocketConnectionClosed, list, messages)
|
||||
|
||||
self.assertTrue(mistral.executions.get.called)
|
||||
websocket.wait_for_messages.assert_called_with(timeout=None)
|
||||
|
||||
def test_wait_for_messages_different_execution(self):
|
||||
payload_a = {
|
||||
'status': 'RUNNING',
|
||||
'execution_id': 'aaaa',
|
||||
'root_execution_id': 'aaaa'
|
||||
}
|
||||
payload_b = {
|
||||
'status': 'RUNNING',
|
||||
'execution_id': 'bbbb',
|
||||
'root_execution_id': 'bbbb'
|
||||
}
|
||||
|
||||
mistral = mock.Mock()
|
||||
mistral.executions.get.return_value.state = 'RUNNING'
|
||||
websocket = mock.Mock()
|
||||
websocket.wait_for_messages.return_value = iter([payload_a, payload_b])
|
||||
execution = mock.Mock()
|
||||
execution.id = 'aaaa'
|
||||
|
||||
messages = list(base.wait_for_messages(mistral, websocket, execution))
|
||||
|
||||
# Assert only payload_a was returned
|
||||
self.assertEqual([payload_a], messages)
|
||||
|
||||
websocket.wait_for_messages.assert_called_with(timeout=None)
|
||||
|
||||
def test_backwards_compat_wait_for_messages_success(self):
|
||||
payload_a = {
|
||||
'status': 'ERROR',
|
||||
'execution': {'id': 2,
|
||||
'root_execution_id': 1}
|
||||
}
|
||||
payload_b = {
|
||||
'status': 'ERROR',
|
||||
'execution': {'id': 1,
|
||||
'root_execution_id': 1}
|
||||
}
|
||||
|
||||
mistral = mock.Mock()
|
||||
websocket = mock.Mock()
|
||||
websocket.wait_for_messages.return_value = iter([payload_a, payload_b])
|
||||
execution = mock.Mock()
|
||||
execution.id = 1
|
||||
|
||||
messages = list(base.wait_for_messages(mistral, websocket, execution))
|
||||
|
||||
self.assertEqual([payload_a, payload_b], messages)
|
||||
|
||||
self.assertFalse(mistral.executions.get.called)
|
||||
websocket.wait_for_messages.assert_called_with(timeout=None)
|
||||
|
||||
def test_backwards_compatible_call_with_different_execution(self):
|
||||
payload_a = {
|
||||
'status': 'RUNNING',
|
||||
'execution': {'id': 'aaaa',
|
||||
'root_execution_id': 'aaaa'}
|
||||
}
|
||||
payload_b = {
|
||||
'status': 'RUNNING',
|
||||
'execution': {'id': 'bbbb',
|
||||
'root_execution_id': 'bbbb'}
|
||||
}
|
||||
|
||||
mistral = mock.Mock()
|
||||
mistral.executions.get.return_value.state = 'RUNNING'
|
||||
websocket = mock.Mock()
|
||||
websocket.wait_for_messages.return_value = iter([payload_a, payload_b])
|
||||
execution = mock.Mock()
|
||||
execution.id = 'aaaa'
|
||||
|
||||
messages = list(base.wait_for_messages(mistral, websocket, execution))
|
||||
|
||||
# Assert only payload_a was returned
|
||||
self.assertEqual([payload_a], messages)
|
||||
|
||||
websocket.wait_for_messages.assert_called_with(timeout=None)
|
||||
|
||||
def test_wait_for_messages_execution_complete(self):
|
||||
payload_a = {
|
||||
'status': 'RUNNING',
|
||||
'execution_id': 'aaaa',
|
||||
'root_execution_id': 'aaaa'
|
||||
}
|
||||
payload_b = {
|
||||
'status': 'SUCCESS',
|
||||
'execution_id': 'aaaa',
|
||||
'root_execution_id': 'aaaa'
|
||||
}
|
||||
|
||||
mistral = mock.Mock()
|
||||
mistral.executions.get.return_value.state = 'SUCCESS'
|
||||
mistral.executions.get.return_value.output = json.dumps(payload_b)
|
||||
websocket = mock.Mock()
|
||||
websocket.wait_for_messages.return_value = iter([payload_a])
|
||||
execution = mock.Mock()
|
||||
execution.id = 'aaaa'
|
||||
|
||||
messages = list(base.wait_for_messages(mistral, websocket, execution))
|
||||
|
||||
# Assert only payload_b was returned
|
||||
self.assertEqual([payload_a, payload_b], messages)
|
||||
mistral.executions.get.assert_called_with('aaaa')
|
||||
|
||||
websocket.wait_for_messages.assert_called_with(timeout=None)
|
|
@ -16,7 +16,6 @@ import mock
|
|||
|
||||
from osc_lib.tests import utils
|
||||
|
||||
from tripleoclient import exceptions
|
||||
from tripleoclient.workflows import parameters
|
||||
|
||||
|
||||
|
@ -39,10 +38,6 @@ class TestParameterWorkflows(utils.TestCommand):
|
|||
self.orchestration = mock.Mock()
|
||||
self.tripleoclient = mock.Mock()
|
||||
self.tripleoclient.object_store = mock.MagicMock()
|
||||
self.websocket = mock.Mock()
|
||||
self.websocket.__enter__ = lambda s: self.websocket
|
||||
self.websocket.__exit__ = lambda s, *exc: None
|
||||
self.tripleoclient.messaging_websocket.return_value = self.websocket
|
||||
self.app.client_manager.tripleoclient = self.tripleoclient
|
||||
self.app.client_manager.orchestration = self.orchestration
|
||||
self.app.client_manager.baremetal = mock.Mock()
|
||||
|
@ -78,39 +73,6 @@ class TestParameterWorkflows(utils.TestCommand):
|
|||
flatten.start()
|
||||
self.addCleanup(flatten.stop)
|
||||
|
||||
@mock.patch('yaml.safe_load')
|
||||
@mock.patch("six.moves.builtins.open")
|
||||
def test_invoke_plan_env_workflows(self, mock_open,
|
||||
mock_safe_load):
|
||||
plan_env_data = {
|
||||
'name': 'overcloud',
|
||||
'workflow_parameters': {
|
||||
'tripleo.derive_params.v1.derive_parameters': {
|
||||
'num_phy_cores_per_numa_node_for_pmd': 2
|
||||
}
|
||||
}
|
||||
}
|
||||
mock_safe_load.return_value = plan_env_data
|
||||
|
||||
self.websocket.wait_for_messages.return_value = iter([{
|
||||
"execution_id": "IDID",
|
||||
"status": "SUCCESS",
|
||||
"message": "",
|
||||
"result": {}
|
||||
}])
|
||||
|
||||
parameters.invoke_plan_env_workflows(
|
||||
self.app.client_manager,
|
||||
'overcloud',
|
||||
'the-plan-environment.yaml')
|
||||
|
||||
self.workflow.executions.create.assert_called_once_with(
|
||||
'tripleo.derive_params.v1.derive_parameters',
|
||||
workflow_input={
|
||||
'plan': 'overcloud',
|
||||
'user_inputs': {
|
||||
'num_phy_cores_per_numa_node_for_pmd': 2}})
|
||||
|
||||
@mock.patch('yaml.safe_load')
|
||||
@mock.patch("six.moves.builtins.open")
|
||||
@mock.patch('tripleoclient.utils.run_ansible_playbook', autospec=True)
|
||||
|
@ -194,67 +156,6 @@ class TestParameterWorkflows(utils.TestCommand):
|
|||
]
|
||||
mock_playbook.assert_has_calls(calls, any_order=True)
|
||||
|
||||
@mock.patch('yaml.safe_load')
|
||||
@mock.patch("six.moves.builtins.open")
|
||||
def test_invoke_plan_env_workflow_failed(self, mock_open,
|
||||
mock_safe_load):
|
||||
plan_env_data = {
|
||||
'name': 'overcloud',
|
||||
'workflow_parameters': {
|
||||
'tripleo.derive_params.v1.derive_parameters': {
|
||||
'num_phy_cores_per_numa_node_for_pmd': 2
|
||||
}
|
||||
}
|
||||
}
|
||||
mock_safe_load.return_value = plan_env_data
|
||||
|
||||
self.websocket.wait_for_messages.return_value = iter([{
|
||||
"execution_id": "IDID",
|
||||
"status": "FAILED",
|
||||
"message": "workflow failure",
|
||||
"result": ""
|
||||
}])
|
||||
|
||||
self.assertRaises(exceptions.PlanEnvWorkflowError,
|
||||
parameters.invoke_plan_env_workflows,
|
||||
self.app.client_manager, 'overcloud',
|
||||
'the-plan-environment.yaml')
|
||||
|
||||
self.workflow.executions.create.assert_called_once_with(
|
||||
'tripleo.derive_params.v1.derive_parameters',
|
||||
workflow_input={
|
||||
'plan': 'overcloud',
|
||||
'user_inputs': {
|
||||
'num_phy_cores_per_numa_node_for_pmd': 2}})
|
||||
|
||||
@mock.patch('yaml.safe_load')
|
||||
@mock.patch("six.moves.builtins.open")
|
||||
def test_invoke_plan_env_workflows_no_workflow_params(
|
||||
self, mock_open, mock_safe_load):
|
||||
plan_env_data = {'name': 'overcloud'}
|
||||
mock_safe_load.return_value = plan_env_data
|
||||
|
||||
parameters.invoke_plan_env_workflows(
|
||||
self.app.client_manager,
|
||||
'overcloud',
|
||||
'the-plan-environment.yaml')
|
||||
|
||||
self.workflow.executions.create.assert_not_called()
|
||||
|
||||
@mock.patch('yaml.safe_load')
|
||||
@mock.patch("six.moves.builtins.open")
|
||||
def test_invoke_plan_env_workflows_no_plan_env_file(
|
||||
self, mock_open, mock_safe_load):
|
||||
|
||||
mock_open.side_effect = IOError('')
|
||||
|
||||
self.assertRaises(exceptions.PlanEnvWorkflowError,
|
||||
parameters.invoke_plan_env_workflows,
|
||||
self.app.client_manager, 'overcloud',
|
||||
'the-plan-environment.yaml')
|
||||
|
||||
self.workflow.executions.create.assert_not_called()
|
||||
|
||||
def test_check_deprecated_params_no_output(self):
|
||||
parameters.check_deprecated_parameters(
|
||||
self.app.client_manager,
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
import mock
|
||||
|
||||
from osc_lib.tests import utils
|
||||
from swiftclient import exceptions as swift_exc
|
||||
|
||||
from tripleoclient import constants
|
||||
from tripleoclient.tests import base
|
||||
|
@ -28,21 +27,27 @@ class TestPlanCreationWorkflows(utils.TestCommand):
|
|||
self.tripleoclient = mock.Mock()
|
||||
self.app.client_manager.tripleoclient = self.tripleoclient
|
||||
self.tripleoclient.object_store.get_account = mock.MagicMock()
|
||||
self.mock_tar = mock.patch(
|
||||
'tripleo_common.utils.tarball.create_tarball',
|
||||
autospec=True
|
||||
)
|
||||
self.mock_tar.start()
|
||||
|
||||
def tearDown(self):
|
||||
super(TestPlanCreationWorkflows, self).tearDown()
|
||||
self.mock_tar.stop()
|
||||
|
||||
@mock.patch("tripleoclient.utils.run_ansible_playbook", autospec=True)
|
||||
@mock.patch('tripleoclient.workflows.plan_management.tarball',
|
||||
autospec=True)
|
||||
@mock.patch('os.chdir', autospec=True)
|
||||
@mock.patch('tempfile.mkdtemp', autospec=True)
|
||||
def test_create_plan_from_templates_success(self, mock_tmp, mock_cd,
|
||||
mock_tarball,
|
||||
mock_run_playbook):
|
||||
plan_management.create_plan_from_templates(
|
||||
self.app.client_manager,
|
||||
'test-overcloud',
|
||||
'/tht-root/')
|
||||
|
||||
mock_run_playbook.assert_called_once_with(
|
||||
mock_run_playbook.assert_called_with(
|
||||
'cli-create-deployment-plan.yaml',
|
||||
'undercloud,',
|
||||
mock.ANY,
|
||||
|
@ -58,12 +63,9 @@ class TestPlanCreationWorkflows(utils.TestCommand):
|
|||
|
||||
@mock.patch("tripleoclient.utils.run_ansible_playbook", autospec=True)
|
||||
@mock.patch('tripleoclient.utils.rel_or_abs_path')
|
||||
@mock.patch('tripleoclient.workflows.plan_management.tarball',
|
||||
autospec=True)
|
||||
@mock.patch('os.chdir', autospec=True)
|
||||
@mock.patch('tempfile.mkdtemp', autospec=True)
|
||||
def test_create_plan_from_templates_roles_data(self, mock_tmp, mock_cd,
|
||||
mock_tarball,
|
||||
mock_norm_path,
|
||||
mock_run_playbook):
|
||||
mock_open_context = mock.mock_open()
|
||||
|
@ -74,7 +76,7 @@ class TestPlanCreationWorkflows(utils.TestCommand):
|
|||
'/tht-root/',
|
||||
'the_roles_file.yaml')
|
||||
|
||||
mock_run_playbook.assert_called_once_with(
|
||||
mock_run_playbook.assert_called_with(
|
||||
'cli-create-deployment-plan.yaml',
|
||||
'undercloud,',
|
||||
mock.ANY,
|
||||
|
@ -91,16 +93,13 @@ class TestPlanCreationWorkflows(utils.TestCommand):
|
|||
self.assertIn(mock.call('the_roles_file.yaml', '/tht-root/'),
|
||||
mock_norm_path.call_args_list)
|
||||
|
||||
self.tripleoclient.object_store.put_object.assert_called_once_with(
|
||||
self.tripleoclient.object_store.put_object.assert_called_with(
|
||||
'test-overcloud', 'roles_data.yaml', mock_open_context())
|
||||
|
||||
@mock.patch("tripleoclient.utils.run_ansible_playbook", autospec=True)
|
||||
@mock.patch('tripleoclient.workflows.plan_management.tarball',
|
||||
autospec=True)
|
||||
@mock.patch('os.chdir', autospec=True)
|
||||
@mock.patch('tempfile.mkdtemp', autospec=True)
|
||||
def test_create_plan_from_templates_plan_env_data(self, mock_tmp, mock_cd,
|
||||
mock_tarball,
|
||||
mock_run_playbook):
|
||||
mock_open_context = mock.mock_open()
|
||||
with mock.patch('six.moves.builtins.open', mock_open_context):
|
||||
|
@ -110,7 +109,7 @@ class TestPlanCreationWorkflows(utils.TestCommand):
|
|||
'/tht-root/',
|
||||
plan_env_file='the-plan-environment.yaml')
|
||||
|
||||
mock_run_playbook.assert_called_once_with(
|
||||
mock_run_playbook.assert_called_with(
|
||||
'cli-create-deployment-plan.yaml',
|
||||
'undercloud,',
|
||||
mock.ANY,
|
||||
|
@ -127,16 +126,13 @@ class TestPlanCreationWorkflows(utils.TestCommand):
|
|||
mock_open_context.assert_has_calls(
|
||||
[mock.call('the-plan-environment.yaml', 'rb')])
|
||||
|
||||
self.tripleoclient.object_store.put_object.assert_called_once_with(
|
||||
self.tripleoclient.object_store.put_object.assert_called_with(
|
||||
'test-overcloud', 'plan-environment.yaml', mock_open_context())
|
||||
|
||||
@mock.patch("tripleoclient.utils.run_ansible_playbook", autospec=True)
|
||||
@mock.patch('tripleoclient.workflows.plan_management.tarball',
|
||||
autospec=True)
|
||||
@mock.patch('os.chdir', autospec=True)
|
||||
@mock.patch('tempfile.mkdtemp', autospec=True)
|
||||
def test_create_plan_from_templates_networks_data(self, mock_tmp, mock_cd,
|
||||
mock_tarball,
|
||||
mock_run_playbook):
|
||||
mock_open_context = mock.mock_open()
|
||||
with mock.patch('six.moves.builtins.open', mock_open_context):
|
||||
|
@ -146,7 +142,7 @@ class TestPlanCreationWorkflows(utils.TestCommand):
|
|||
'/tht-root/',
|
||||
networks_file='the-network-data.yaml')
|
||||
|
||||
mock_run_playbook.assert_called_once_with(
|
||||
mock_run_playbook.assert_called_with(
|
||||
'cli-create-deployment-plan.yaml',
|
||||
'undercloud,',
|
||||
mock.ANY,
|
||||
|
@ -162,16 +158,13 @@ class TestPlanCreationWorkflows(utils.TestCommand):
|
|||
mock_open_context.assert_has_calls(
|
||||
[mock.call('the-network-data.yaml', 'rb')])
|
||||
|
||||
self.tripleoclient.object_store.put_object.assert_called_once_with(
|
||||
self.tripleoclient.object_store.put_object.assert_called_with(
|
||||
'test-overcloud', 'network_data.yaml', mock_open_context())
|
||||
|
||||
@mock.patch("tripleoclient.utils.run_ansible_playbook", autospec=True)
|
||||
@mock.patch('tripleoclient.workflows.plan_management.tarball',
|
||||
autospec=True)
|
||||
@mock.patch('os.chdir', autospec=True)
|
||||
@mock.patch('tempfile.mkdtemp', autospec=True)
|
||||
def test_create_plan_with_password_gen_disabled(self, mock_tmp, mock_cd,
|
||||
mock_tarball,
|
||||
mock_run_playbook):
|
||||
plan_management.create_plan_from_templates(
|
||||
self.app.client_manager,
|
||||
|
@ -180,7 +173,7 @@ class TestPlanCreationWorkflows(utils.TestCommand):
|
|||
generate_passwords=False,
|
||||
disable_image_params_prepare=True)
|
||||
|
||||
mock_run_playbook.assert_called_once_with(
|
||||
mock_run_playbook.assert_called_with(
|
||||
'cli-create-deployment-plan.yaml',
|
||||
'undercloud,',
|
||||
mock.ANY,
|
||||
|
@ -226,16 +219,23 @@ class TestPlanUpdateWorkflows(base.TestCommand):
|
|||
# e.g. 'plan-environment.yaml: mock content'
|
||||
return {}, '{0}: mock content\n'.format(args[1])
|
||||
self.object_store.get_object.side_effect = get_object
|
||||
self.mock_tar = mock.patch(
|
||||
'tripleo_common.utils.tarball.create_tarball',
|
||||
autospec=True
|
||||
)
|
||||
self.mock_tar.start()
|
||||
|
||||
def tearDown(self):
|
||||
super(TestPlanUpdateWorkflows, self).tearDown()
|
||||
self.mock_tar.stop()
|
||||
|
||||
@mock.patch("tripleoclient.utils.run_ansible_playbook", autospec=True)
|
||||
@mock.patch('tripleoclient.workflows.plan_management.tarball',
|
||||
autospec=True)
|
||||
@mock.patch('tripleo_common.utils.swift.empty_container',
|
||||
autospec=True)
|
||||
@mock.patch('os.chdir', autospec=True)
|
||||
@mock.patch('tempfile.mkdtemp', autospec=True)
|
||||
def test_update_plan_from_templates_keep_env(
|
||||
self, mock_tmp, mock_cd, mock_empty_container, mock_tarball,
|
||||
self, mock_tmp, mock_cd, mock_empty_container,
|
||||
mock_run_playbook):
|
||||
|
||||
plan_management.update_plan_from_templates(
|
||||
|
@ -266,7 +266,7 @@ class TestPlanUpdateWorkflows(base.TestCommand):
|
|||
],
|
||||
any_order=True,
|
||||
)
|
||||
mock_run_playbook.assert_called_once_with(
|
||||
mock_run_playbook.assert_called_with(
|
||||
'cli-update-deployment-plan.yaml',
|
||||
'undercloud,',
|
||||
mock.ANY,
|
||||
|
@ -280,12 +280,10 @@ class TestPlanUpdateWorkflows(base.TestCommand):
|
|||
)
|
||||
|
||||
@mock.patch("tripleoclient.utils.run_ansible_playbook", autospec=True)
|
||||
@mock.patch('tripleoclient.workflows.plan_management.tarball',
|
||||
autospec=True)
|
||||
@mock.patch('tripleo_common.utils.swift.empty_container',
|
||||
autospec=True)
|
||||
def test_update_plan_from_templates_recreate_env(
|
||||
self, mock_empty_container, mock_tarball, mock_run_playbook):
|
||||
self, mock_empty_container, mock_run_playbook):
|
||||
|
||||
plan_management.update_plan_from_templates(
|
||||
self.app.client_manager,
|
||||
|
@ -302,7 +300,7 @@ class TestPlanUpdateWorkflows(base.TestCommand):
|
|||
'plan-environment.yaml: mock content\n'
|
||||
)
|
||||
|
||||
mock_run_playbook.assert_called_once_with(
|
||||
mock_run_playbook.assert_called_with(
|
||||
'cli-update-deployment-plan.yaml',
|
||||
'undercloud,',
|
||||
mock.ANY,
|
||||
|
@ -320,14 +318,12 @@ class TestPlanUpdateWorkflows(base.TestCommand):
|
|||
autospec=True)
|
||||
@mock.patch('yaml.safe_load',
|
||||
autospec=True)
|
||||
@mock.patch('tripleoclient.workflows.plan_management.tarball',
|
||||
autospec=True)
|
||||
@mock.patch('tripleo_common.utils.swift.empty_container',
|
||||
autospec=True)
|
||||
@mock.patch('os.chdir', autospec=True)
|
||||
@mock.patch('tempfile.mkdtemp', autospec=True)
|
||||
def test_update_plan_from_templates_recreate_env_missing_passwords(
|
||||
self, mock_tmp, mock_cd, mock_empty_container, mock_tarball,
|
||||
self, mock_tmp, mock_cd, mock_empty_container,
|
||||
mock_yaml_safe_load, mock_update_passwords, mock_run_playbook):
|
||||
plan_management.update_plan_from_templates(
|
||||
self.app.client_manager,
|
||||
|
@ -340,7 +336,7 @@ class TestPlanUpdateWorkflows(base.TestCommand):
|
|||
# Ensure that the passwords variable is passed with a value of None.
|
||||
mock_update_passwords.assert_called_with(
|
||||
mock.ANY, 'test-overcloud', None)
|
||||
mock_run_playbook.assert_called_once_with(
|
||||
mock_run_playbook.assert_called_with(
|
||||
'cli-update-deployment-plan.yaml',
|
||||
'undercloud,',
|
||||
mock.ANY,
|
||||
|
@ -375,7 +371,6 @@ parameter_defaults:
|
|||
self.plan_name,
|
||||
{'AdminPassword': "1234"})
|
||||
|
||||
self.swift_client.put_object.assert_called_once()
|
||||
result = self.swift_client.put_object.call_args_list[0][0][2]
|
||||
|
||||
# Check new data is in
|
||||
|
@ -393,7 +388,7 @@ parameter_defaults:
|
|||
|
||||
def test_no_plan_environment(self):
|
||||
self.swift_client.get_object.side_effect = (
|
||||
swift_exc.ClientException("404"))
|
||||
Exception("404"))
|
||||
|
||||
plan_management._update_passwords(self.swift_client,
|
||||
self.plan_name,
|
||||
|
|
|
@ -59,6 +59,7 @@ from heatclient import exc as hc_exc
|
|||
from six.moves.urllib import error as url_error
|
||||
from six.moves.urllib import request
|
||||
|
||||
from tripleo_common.utils import swift as swiftutils
|
||||
from tripleoclient import constants
|
||||
from tripleoclient import exceptions
|
||||
|
||||
|
@ -2361,12 +2362,12 @@ def update_deployment_status(clients, plan, status):
|
|||
|
||||
# create {plan}-messages container if not there
|
||||
swift = clients.tripleoclient.object_store
|
||||
swift.put_container(container)
|
||||
|
||||
swift.put_object(
|
||||
container,
|
||||
'deployment_status.yaml',
|
||||
yaml.safe_dump(
|
||||
swiftutils.create_container(swiftclient=swift, container=container)
|
||||
swiftutils.put_object_string(
|
||||
swift=swift,
|
||||
container=container,
|
||||
object_name='deployment_status.yaml',
|
||||
contents=yaml.safe_dump(
|
||||
{
|
||||
'deployment_status': status,
|
||||
'workflow_status': {
|
||||
|
|
|
@ -34,7 +34,6 @@ from keystoneauth1.exceptions.catalog import EndpointNotFound
|
|||
import openstack
|
||||
from osc_lib import exceptions as oscexc
|
||||
from osc_lib.i18n import _
|
||||
from swiftclient.exceptions import ClientException
|
||||
from tripleo_common import update
|
||||
|
||||
from tripleoclient import command
|
||||
|
@ -504,7 +503,7 @@ class DeployOvercloud(command.Command):
|
|||
user_env = yaml.safe_load(self.object_client.get_object(
|
||||
parsed_args.stack, constants.USER_ENVIRONMENT)[1])
|
||||
template_utils.deep_update(env, user_env)
|
||||
except ClientException:
|
||||
except Exception:
|
||||
pass
|
||||
parameters.update(self._update_parameters(parsed_args, stack))
|
||||
template_utils.deep_update(env, self._create_parameters_env(
|
||||
|
@ -589,7 +588,7 @@ class DeployOvercloud(command.Command):
|
|||
run_validations, skip_deploy_identifier,
|
||||
plan_env_file,
|
||||
deployment_options=deployment_options)
|
||||
except ClientException as e:
|
||||
except Exception as e:
|
||||
messages = 'Failed to deploy: %s' % str(e)
|
||||
raise ValueError(messages)
|
||||
|
||||
|
|
|
@ -1,118 +0,0 @@
|
|||
# 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 json
|
||||
import keystoneauth1
|
||||
import logging
|
||||
|
||||
from tripleoclient import exceptions
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def start_workflow(workflow_client, identifier, workflow_input):
|
||||
|
||||
execution = workflow_client.executions.create(
|
||||
identifier,
|
||||
workflow_input=workflow_input
|
||||
)
|
||||
|
||||
LOG.debug("Started Mistral Workflow {}. Execution ID: {}".format(
|
||||
identifier, execution.id))
|
||||
|
||||
return execution
|
||||
|
||||
|
||||
def wait_for_messages(mistral, websocket, execution, timeout=None):
|
||||
"""Wait for messages on a websocket.
|
||||
|
||||
Given an instance of mistral client, a websocket and a Mistral execution
|
||||
wait for messages on that websocket queue that match the execution ID until
|
||||
the timeout is reached.
|
||||
|
||||
If no timeout is provided, this method will block forever.
|
||||
|
||||
If a timeout is reached, called check_execution_status which will look up
|
||||
the execution on Mistral and log information about it.
|
||||
"""
|
||||
try:
|
||||
for payload in websocket.wait_for_messages(timeout=timeout):
|
||||
# Ignore messages whose root_execution_id does not match the
|
||||
# id of the execution for which we are waiting
|
||||
|
||||
# New versions of tripleo-common don't sent the execution anymore
|
||||
# but keeping the old way ot getting it is important to keep
|
||||
# backwards compatibility.
|
||||
|
||||
# TODO(apetrich) payload.execution is deprecated and will be
|
||||
# removed from stein. We should keep this until payload.execution
|
||||
# is removed from the LTS
|
||||
payload_exec_id = payload.get('execution_id') or \
|
||||
payload.get('execution', {}).get('id')
|
||||
|
||||
payload_root_exec_id = payload.get('root_execution_id', '') or \
|
||||
payload.get('execution', {}).get('root_execution_id', '')
|
||||
|
||||
if payload_exec_id != execution.id and \
|
||||
payload_root_exec_id != execution.id:
|
||||
|
||||
LOG.debug("Ignoring message from execution %s"
|
||||
% payload_exec_id)
|
||||
else:
|
||||
yield payload
|
||||
# If the message is from a sub-workflow, we just need to pass it
|
||||
# on to be displayed. This should never be the last message - so
|
||||
# continue and wait for the next.
|
||||
if payload_exec_id != execution.id:
|
||||
continue
|
||||
# Check the status of the payload, if we are not given one
|
||||
# default to running and assume it is just an "in progress"
|
||||
# message from the workflow.
|
||||
# Workflows should end with SUCCESS or ERROR statuses.
|
||||
if payload.get('status', 'RUNNING') != "RUNNING":
|
||||
return
|
||||
try:
|
||||
execution = mistral.executions.get(execution.id)
|
||||
except keystoneauth1.exceptions.connection.ConnectFailure as e:
|
||||
LOG.warning("Connection failure while fetching execution ID."
|
||||
"Retrying: %s" % e)
|
||||
continue
|
||||
|
||||
if execution.state != "RUNNING":
|
||||
# yield the output as the last payload which was missed
|
||||
yield json.loads(execution.output)
|
||||
return
|
||||
except (exceptions.WebSocketTimeout, exceptions.WebSocketConnectionClosed):
|
||||
check_execution_status(mistral, execution.id)
|
||||
raise
|
||||
|
||||
|
||||
def check_execution_status(workflow_client, execution_id):
|
||||
"""Check the status of a workflow that timeout when waiting for messages
|
||||
|
||||
The status will be logged.
|
||||
"""
|
||||
|
||||
execution = workflow_client.executions.get(execution_id)
|
||||
state = execution.state
|
||||
|
||||
if state == 'RUNNING':
|
||||
message = "The WebSocket timed out before the Workflow completed."
|
||||
elif state == 'SUCCESS':
|
||||
message = ("The Workflow finished successfully but no messages were "
|
||||
"received before the WebSocket timed out.")
|
||||
elif state == 'ERROR':
|
||||
message = "The Workflow errored and no messages were received."
|
||||
else:
|
||||
message = "Unknown Execution state."
|
||||
|
||||
LOG.error(("Timed out waiting for messages from Execution "
|
||||
"(ID: {}, State: {}). {}").format(execution_id, state, message))
|
|
@ -18,8 +18,7 @@ import yaml
|
|||
from heatclient.common import event_utils
|
||||
from heatclient import exc as heat_exc
|
||||
from openstackclient import shell
|
||||
from swiftclient import exceptions as swiftexceptions
|
||||
from tripleo_common.utils import swift as swift_utils
|
||||
from tripleo_common.utils import swift as swiftutils
|
||||
|
||||
from tripleoclient.constants import ANSIBLE_TRIPLEO_PLAYBOOKS
|
||||
from tripleoclient.constants import CLOUD_HOME_DIR
|
||||
|
@ -515,13 +514,13 @@ def get_deployment_status(clients, plan):
|
|||
return None
|
||||
|
||||
try:
|
||||
body = swift_utils.get_object_string(
|
||||
body = swiftutils.get_object_string(
|
||||
clients.tripleoclient.object_store,
|
||||
'%s-messages' % plan,
|
||||
'deployment_status.yaml')
|
||||
|
||||
return yaml.safe_load(body)['deployment_status']
|
||||
except swiftexceptions.ClientException:
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@ from tripleoclient.constants import ANSIBLE_TRIPLEO_PLAYBOOKS
|
|||
from tripleoclient.constants import UNUSED_PARAMETER_EXCLUDES_RE
|
||||
from tripleoclient import exceptions
|
||||
from tripleoclient import utils
|
||||
from tripleoclient.workflows import base
|
||||
from tripleoclient.workflows import roles
|
||||
|
||||
|
||||
|
@ -39,73 +38,37 @@ def invoke_plan_env_workflows(clients, stack_name, plan_env_file,
|
|||
raise exceptions.PlanEnvWorkflowError('File (%s) is not found: '
|
||||
'%s' % (plan_env_file, exc))
|
||||
|
||||
if plan_env_data and "playbook_parameters" in plan_env_data:
|
||||
static_inventory = utils.get_tripleo_ansible_inventory(
|
||||
ssh_user='heat-admin',
|
||||
stack=stack_name,
|
||||
undercloud_connection='local',
|
||||
return_inventory_file_path=True
|
||||
)
|
||||
with utils.TempDirs() as tmp:
|
||||
for pb, pb_vars in plan_env_data["playbook_parameters"].items():
|
||||
print('Invoking playbook ({}) specified in plan-environment'
|
||||
' file'.format(pb))
|
||||
LOG.debug(
|
||||
'Running playbook "{}" with the'
|
||||
' following options {}.'.format(
|
||||
pb,
|
||||
pb_vars
|
||||
)
|
||||
static_inventory = utils.get_tripleo_ansible_inventory(
|
||||
ssh_user='heat-admin',
|
||||
stack=stack_name,
|
||||
undercloud_connection='local',
|
||||
return_inventory_file_path=True
|
||||
)
|
||||
with utils.TempDirs() as tmp:
|
||||
for pb, pb_vars in plan_env_data["playbook_parameters"].items():
|
||||
print(
|
||||
'Invoking playbook ({}) specified in'
|
||||
' plan-environment file'.format(pb)
|
||||
)
|
||||
LOG.debug(
|
||||
'Running playbook "{}" with the'
|
||||
' following options {}.'.format(
|
||||
pb,
|
||||
pb_vars
|
||||
)
|
||||
playbook_dir = os.path.dirname(pb)
|
||||
if not playbook_dir:
|
||||
playbook_dir = ANSIBLE_TRIPLEO_PLAYBOOKS
|
||||
)
|
||||
playbook_dir = os.path.dirname(pb)
|
||||
if not playbook_dir:
|
||||
playbook_dir = ANSIBLE_TRIPLEO_PLAYBOOKS
|
||||
|
||||
utils.run_ansible_playbook(
|
||||
playbook=os.path.basename(pb),
|
||||
inventory=static_inventory,
|
||||
workdir=tmp,
|
||||
playbook_dir=playbook_dir,
|
||||
verbosity=verbosity,
|
||||
extra_vars=pb_vars
|
||||
)
|
||||
|
||||
# NOTE(cloudnull): Remove this when mistral is gone.
|
||||
elif plan_env_data and "workflow_parameters" in plan_env_data:
|
||||
LOG.warning(
|
||||
'The workflow_parameters interface is deprecated, begin using'
|
||||
' playbook_parameters instead.'
|
||||
)
|
||||
for wf_name, wf_inputs in plan_env_data["workflow_parameters"].items():
|
||||
print('Invoking workflow (%s) specified in plan-environment '
|
||||
'file' % wf_name)
|
||||
inputs = {'plan': stack_name, 'user_inputs': wf_inputs}
|
||||
workflow_client = clients.workflow_engine
|
||||
tripleoclients = clients.tripleoclient
|
||||
with tripleoclients.messaging_websocket() as ws:
|
||||
execution = base.start_workflow(
|
||||
workflow_client,
|
||||
wf_name,
|
||||
workflow_input=inputs
|
||||
)
|
||||
|
||||
# Getting the derive parameters timeout after 600 seconds.
|
||||
for payload in base.wait_for_messages(workflow_client,
|
||||
ws, execution, 600):
|
||||
if ('message' in payload and
|
||||
(payload.get('status', 'RUNNING') == "RUNNING")):
|
||||
print(payload['message'])
|
||||
|
||||
if payload.get('status', 'FAILED') == 'SUCCESS':
|
||||
result = payload.get('result', '')
|
||||
# Prints the workflow result
|
||||
if result:
|
||||
print('Workflow execution is completed. result:')
|
||||
print(yaml.safe_dump(result, default_flow_style=False))
|
||||
else:
|
||||
message = payload.get('message', '')
|
||||
msg = ('Workflow execution is failed: %s' % (message))
|
||||
raise exceptions.PlanEnvWorkflowError(msg)
|
||||
utils.run_ansible_playbook(
|
||||
playbook=os.path.basename(pb),
|
||||
inventory=static_inventory,
|
||||
workdir=tmp,
|
||||
playbook_dir=playbook_dir,
|
||||
verbosity=verbosity,
|
||||
extra_vars=pb_vars
|
||||
)
|
||||
|
||||
|
||||
def check_deprecated_parameters(clients, container):
|
||||
|
|
|
@ -11,14 +11,12 @@
|
|||
# under the License.
|
||||
import logging
|
||||
import os
|
||||
import tempfile
|
||||
import yaml
|
||||
|
||||
from swiftclient import exceptions as swift_exc
|
||||
from tripleo_common.actions import plan
|
||||
from tripleo_common.utils import plan as plan_utils
|
||||
from tripleo_common.utils import swift as swiftutils
|
||||
from tripleo_common.utils import tarball
|
||||
from tripleo_common.utils import template as temputils
|
||||
|
||||
from tripleoclient import constants
|
||||
from tripleoclient import exceptions
|
||||
|
@ -39,10 +37,11 @@ def _upload_templates(swift_client, container_name, tht_root, roles_file=None,
|
|||
plan_env_file=None, networks_file=None):
|
||||
"""tarball up a given directory and upload it to Swift to be extracted"""
|
||||
|
||||
with tempfile.NamedTemporaryFile() as tmp_tarball:
|
||||
tarball.create_tarball(tht_root, tmp_tarball.name)
|
||||
tarball.tarball_extract_to_swift_container(
|
||||
swift_client, tmp_tarball.name, container_name)
|
||||
temputils.upload_templates_as_tarball(
|
||||
swift=swift_client,
|
||||
dir_to_upload=tht_root,
|
||||
container=container_name
|
||||
)
|
||||
|
||||
# Optional override of the roles_data.yaml file
|
||||
if roles_file:
|
||||
|
@ -183,6 +182,19 @@ def create_plan_from_templates(clients, name, tht_root, roles_file=None,
|
|||
generate_passwords=True, plan_env_file=None,
|
||||
networks_file=None, verbosity_level=0,
|
||||
disable_image_params_prepare=False):
|
||||
|
||||
with utils.TempDirs() as tmp:
|
||||
utils.run_ansible_playbook(
|
||||
"cli-undercloud-local-artifacts.yaml",
|
||||
'undercloud,',
|
||||
workdir=tmp,
|
||||
playbook_dir=constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
|
||||
extra_vars={
|
||||
"stack": name,
|
||||
},
|
||||
verbosity=verbosity_level
|
||||
)
|
||||
|
||||
swift_client = clients.tripleoclient.object_store
|
||||
|
||||
print("Creating Swift container to store the plan")
|
||||
|
@ -210,6 +222,19 @@ def update_plan_from_templates(clients, name, tht_root, roles_file=None,
|
|||
networks_file=None, keep_env=False,
|
||||
verbosity_level=1,
|
||||
disable_image_params_prepare=False):
|
||||
|
||||
with utils.TempDirs() as tmp:
|
||||
utils.run_ansible_playbook(
|
||||
"cli-undercloud-local-artifacts.yaml",
|
||||
'undercloud,',
|
||||
workdir=tmp,
|
||||
playbook_dir=constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
|
||||
extra_vars={
|
||||
"stack": name,
|
||||
},
|
||||
verbosity=verbosity_level
|
||||
)
|
||||
|
||||
swift_client = clients.tripleoclient.object_store
|
||||
passwords = None
|
||||
keep_file_contents = {}
|
||||
|
@ -308,13 +333,23 @@ def _list_plan_files(swift_client, container):
|
|||
|
||||
def _upload_file(swift_client, container, filename, local_filename):
|
||||
with open(local_filename, 'rb') as file_content:
|
||||
swift_client.put_object(container, filename, file_content)
|
||||
swiftutils.put_object_string(
|
||||
swift=swift_client,
|
||||
container=container,
|
||||
object_name=filename,
|
||||
contents=file_content
|
||||
)
|
||||
|
||||
|
||||
# short function, just alias for interface parity with _upload_plan_file
|
||||
def _upload_file_content(swift_client, container, filename, content):
|
||||
LOG.debug("Uploading {0} to plan".format(filename))
|
||||
swift_client.put_object(container, filename, content)
|
||||
swiftutils.put_object_string(
|
||||
swift=swift_client,
|
||||
container=container,
|
||||
object_name=filename,
|
||||
contents=content
|
||||
)
|
||||
|
||||
|
||||
def _load_passwords(swift_client, name):
|
||||
|
@ -337,31 +372,41 @@ def _update_passwords(swift_client, name, passwords):
|
|||
env = yaml.safe_load(swift_client.get_object(
|
||||
name, constants.PLAN_ENVIRONMENT)[1])
|
||||
env['passwords'] = passwords
|
||||
swift_client.put_object(name,
|
||||
constants.PLAN_ENVIRONMENT,
|
||||
yaml.safe_dump(env,
|
||||
default_flow_style=False))
|
||||
except swift_exc.ClientException:
|
||||
swiftutils.put_object_string(
|
||||
swift=swift_client,
|
||||
container=name,
|
||||
object_name=constants.PLAN_ENVIRONMENT,
|
||||
contents=yaml.safe_dump(env, default_flow_style=False)
|
||||
)
|
||||
except Exception as exp:
|
||||
# The plan likely has not been migrated to using Swift yet.
|
||||
LOG.debug("Could not find plan environment %s in %s",
|
||||
constants.PLAN_ENVIRONMENT, name)
|
||||
LOG.debug("Could not find plan environment %s in %s - error: %s",
|
||||
constants.PLAN_ENVIRONMENT, name, exp)
|
||||
|
||||
|
||||
def export_deployment_plan(clients, plan_name):
|
||||
|
||||
export_container = "plan-exports"
|
||||
delete_after = 3600
|
||||
plan_path = os.path.join('/var/lib/tripleo/stacks', plan_name)
|
||||
if os.path.exists(plan_path):
|
||||
print("Plan information can be found here: {}".format(plan_path))
|
||||
return plan_path
|
||||
else:
|
||||
# NOTE(cloudnull): When swift is no longer in service remove this.
|
||||
export_container = "plan-exports"
|
||||
delete_after = 3600
|
||||
|
||||
mistral_context = clients.tripleoclient.create_mistral_context()
|
||||
action = plan.ExportPlanAction(plan_name, delete_after=delete_after,
|
||||
exports_container=export_container)
|
||||
result = action.run(mistral_context)
|
||||
if result:
|
||||
raise exceptions.WorkflowServiceError(
|
||||
'Exception exporting plan: {}'.format(result.error))
|
||||
mistral_context = clients.tripleoclient.create_mistral_context()
|
||||
action = plan.ExportPlanAction(plan_name, delete_after=delete_after,
|
||||
exports_container=export_container)
|
||||
result = action.run(mistral_context)
|
||||
if result:
|
||||
raise exceptions.WorkflowServiceError(
|
||||
'Exception exporting plan: {}'.format(result.error))
|
||||
|
||||
url = swiftutils.get_temp_url(clients.tripleoclient.object_store,
|
||||
container=export_container,
|
||||
object_name="{}.tar.gz".format(plan_name))
|
||||
print(url)
|
||||
return url
|
||||
url = swiftutils.get_temp_url(
|
||||
clients.tripleoclient.object_store,
|
||||
container=export_container,
|
||||
object_name="{}.tar.gz".format(plan_name)
|
||||
)
|
||||
print(url)
|
||||
return url
|
||||
|
|
|
@ -15,6 +15,8 @@ import logging
|
|||
import yaml
|
||||
|
||||
from tripleo_common.actions import plan
|
||||
# TODO(cloudnull): Convert to a swiftutils in tripleo-common
|
||||
# from tripleo_common.utils import swift as swiftutils
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
@ -36,6 +38,7 @@ def list_available_roles(clients, container='overcloud'):
|
|||
obj_client = clients.tripleoclient.object_store
|
||||
available_yaml_roles = list()
|
||||
LOG.info('Indexing roles from: {}'.format(container))
|
||||
# TODO(cloudnull): Convert to a swiftutils in tripleo-common
|
||||
for obj in obj_client.get_container(container)[-1]:
|
||||
name = obj['name']
|
||||
if name.startswith('roles/') and name.endswith(('yml', 'yaml')):
|
||||
|
|
Loading…
Reference in New Issue