diff --git a/etc/agent-config/Default.template b/etc/agent-config/Default.template
new file mode 100644
index 0000000..839abe8
--- /dev/null
+++ b/etc/agent-config/Default.template
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/etc/agent-config/Demo.template b/etc/agent-config/Demo.template
new file mode 100644
index 0000000..8fec1df
--- /dev/null
+++ b/etc/agent-config/Demo.template
@@ -0,0 +1,8 @@
+RABBITMQ_HOST = "%RABBITMQ_HOST%"
+RABBITMQ_PORT = "%RABBITMQ_PORT%"
+RABBITMQ_USERNAME = "%RABBITMQ_USER%"
+RABBITMQ_PASSWORD = "%RABBITMQ_PASSWORD%"
+RABBITMQ_VHOST = "%RABBITMQ_VHOST%"
+RABBITMQ_INPUT_QUEUE = "%RABBITMQ_INPUT_QUEUE%"
+RESULT_QUEUE = "%RESULT_QUEUE%"
+RABBITMQ_RESULT_ROUTING_KEY = "%RESULT_QUEUE%"
diff --git a/etc/agent-config/Linux.template b/etc/agent-config/Linux.template
new file mode 100644
index 0000000..b1f3db9
--- /dev/null
+++ b/etc/agent-config/Linux.template
@@ -0,0 +1,35 @@
+[DEFAULT]
+debug=True
+verbose=True
+log_file = /var/log/murano-agnet.log
+
+storage=/var/murano/plans
+
+[rabbitmq]
+
+# Input queue name
+input_queue = %RABBITMQ_INPUT_QUEUE%
+
+# Output routing key (usually queue name)
+result_routing_key = %RESULT_QUEUE%
+
+# Connection parameters to RabbitMQ service
+
+# Hostname or IP address where RabbitMQ is located.
+host = %RABBITMQ_HOST%
+
+# RabbitMQ port (5672 is a default)
+port = %RABBITMQ_PORT%
+
+# Use SSL for RabbitMQ connections (True or False)
+ssl = %RABBITMQ_SSL%
+
+# Path to SSL CA certificate or empty to allow self signed server certificate
+ca_certs =
+
+# RabbitMQ credentials. Fresh RabbitMQ installation has "guest" account with "guest" password.
+login = %RABBITMQ_USER%
+password = %RABBITMQ_PASSWORD%
+
+# RabbitMQ virtual host (vhost). Fresh RabbitMQ installation has "/" vhost preconfigured.
+virtual_host = %RABBITMQ_VHOST%
diff --git a/etc/conductor.conf b/etc/conductor.conf
index 6c346f5..bf1417f 100644
--- a/etc/conductor.conf
+++ b/etc/conductor.conf
@@ -7,9 +7,18 @@ log_file = /tmp/conductor.log
debug=True
verbose=True
-# Directory where conductor's data directory located.
-# "data" must be subdirectory to this.
-data_dir = /etc/murano-conductor
+# Provide directory with initialization scripts
+init_scripts_dir = ./etc/init-scripts
+
+# Provide directory with agent configs
+agent_config_dir = ./etc/agent-config
+
+# Provide absolute or relative path to data storing
+# directory (may not be exist)
+data_dir = test_data
+
+# Provide url to Murano Metadata repository
+murano_metadata_url = http://localhost:8084
# Maximum number of environments that can be processed simultaneously
max_environments = 20
@@ -45,7 +54,7 @@ endpoint_type = publicURL
[rabbitmq]
# Connection parameters to RabbitMQ service
-# Hostname or IP address where RabbitMQ is located.
+# Hostname or IP address where RabbitMQ is located.
# !!! Change localhost to your real IP or hostname as this address must be reachable from VMs !!!
host = localhost
diff --git a/etc/init-scripts/demo_init.sh b/etc/init-scripts/demo_init.sh
new file mode 100644
index 0000000..c1cfe24
--- /dev/null
+++ b/etc/init-scripts/demo_init.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+AgentConfigBase64='%AGENT_CONFIG_BASE64%'
+
+mkdir /etc/murano
+
+echo $AgentConfigBase64 | base64 -d > /etc/murano/agent.config
+
+chmod 664 /etc/murano/agent.config
+sleep 10
+reboot
diff --git a/etc/init-scripts/init.ps1 b/etc/init-scripts/init.ps1
new file mode 100644
index 0000000..9fbba22
--- /dev/null
+++ b/etc/init-scripts/init.ps1
@@ -0,0 +1,68 @@
+#ps1
+
+$WindowsAgentConfigBase64 = '%AGENT_CONFIG_BASE64%'
+$WindowsAgentConfigFile = "C:\Murano\Agent\WindowsAgent.exe.config"
+$WindowsAgentLogFile = "C:\Murano\Agent\log.txt"
+
+$NewComputerName = '%INTERNAL_HOSTNAME%'
+$MuranoFileShare = '\\%MURANO_SERVER_ADDRESS%\share'
+
+$CaRootCertBase64 = "%CA_ROOT_CERT_BASE64%"
+$CaRootCertFile = "C:\Murano\ca.cert"
+
+$RestartRequired = $false
+
+Import-Module CoreFunctions
+Initialize-Logger 'CloudBase-Init' 'C:\Murano\PowerShell.log'
+
+$ErrorActionPreference = 'Stop'
+
+trap {
+ Write-LogError ''
+ Write-LogError $_ -EntireObject
+ Write-LogError ''
+ exit 1
+}
+
+Write-Log "Importing CA certificate ..."
+if ($CaRootCertBase64 -eq '') {
+ Write-Log "Importing CA certificate ... skipped"
+}
+else {
+ ConvertFrom-Base64String -Base64String $CaRootCertBase64 -Path $CaRootCertFile
+ $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 $CaRootCertFile
+ $store = New-Object System.Security.Cryptography.X509Certificates.X509Store("AuthRoot","LocalMachine")
+ $store.Open("MaxAllowed")
+ $store.Add($cert)
+ $store.Close()
+ Write-Log "Importing CA certificate ... done"
+}
+
+Write-Log "Updating Murano Windows Agent."
+Stop-Service "Murano Agent"
+Backup-File $WindowsAgentConfigFile
+Remove-Item $WindowsAgentConfigFile -Force
+Remove-Item $WindowsAgentLogFile -Force
+ConvertFrom-Base64String -Base64String $WindowsAgentConfigBase64 -Path $WindowsAgentConfigFile
+Exec sc.exe 'config','"Murano Agent"','start=','delayed-auto'
+Write-Log "Service has been updated."
+
+Write-Log "Adding environment variable 'MuranoFileShare' = '$MuranoFileShare' ..."
+[Environment]::SetEnvironmentVariable('MuranoFileShare', $MuranoFileShare, [EnvironmentVariableTarget]::Machine)
+Write-Log "Environment variable added."
+
+Write-Log "Renaming computer to '$NewComputerName' ..."
+$null = Rename-Computer -NewName $NewComputerName -Force
+
+Write-Log "New name assigned, restart required."
+$RestartRequired = $true
+
+
+Write-Log 'All done!'
+if ( $RestartRequired ) {
+ Write-Log "Restarting computer ..."
+ Restart-Computer -Force
+}
+else {
+ Start-Service 'Murano Agent'
+}
diff --git a/etc/init-scripts/linux_init.sh b/etc/init-scripts/linux_init.sh
new file mode 100644
index 0000000..e9f8ffd
--- /dev/null
+++ b/etc/init-scripts/linux_init.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+AgentConfigBase64='%AGENT_CONFIG_BASE64%'
+service murano-agent stop
+echo $AgentConfigBase64 | base64 -d > /etc/murano-agent.conf
+service murano-agent start
diff --git a/heatplugin/murano/environment.py b/heatplugin/murano/environment.py
index a748d8d..35f08f2 100644
--- a/heatplugin/murano/environment.py
+++ b/heatplugin/murano/environment.py
@@ -47,7 +47,6 @@ class MuranoEnvironment(resource.Resource):
except HTTPNotFound:
pass
-
def _find_environment(self, client):
environments = client.environments.list()
for environment in environments:
@@ -75,10 +74,10 @@ class MuranoEnvironment(resource.Resource):
delay = 2
while True:
environment = client.environments.get(environment_id)
- if environment.status == 'pending' and i > 5*60:
+ if environment.status == 'pending' and i > 5 * 60:
raise EnvironmentError(
"Environment deployment hasn't started")
- elif environment.status == 'deploying' and i > 65*60:
+ elif environment.status == 'deploying' and i > 65 * 60:
raise EnvironmentError(
"Environment deployment takes too long")
elif environment.status == 'ready':
diff --git a/muranoconductor/app.py b/muranoconductor/app.py
index 90e4d5b..6310606 100644
--- a/muranoconductor/app.py
+++ b/muranoconductor/app.py
@@ -15,7 +15,6 @@
import glob
import sys
-import traceback
import anyjson
import eventlet
@@ -28,7 +27,7 @@ import reporting
from muranocommon.messaging import MqClient, Message
from muranoconductor import config as cfg
from muranocommon.helpers.token_sanitizer import TokenSanitizer
-
+from muranoconductor import metadata
import vm_agent
import cloud_formation
@@ -80,23 +79,29 @@ class ConductorWorkflowService(service.Service):
message_id = message.id
do_ack = False
reporter = None
+
with self.create_rmq_client() as mq:
try:
+
secure_task = TokenSanitizer().sanitize(task)
log.info('Starting processing task {0}: {1}'.format(
message_id, anyjson.dumps(secure_task)))
reporter = reporting.Reporter(mq, message_id, task['id'])
- config = Config()
+ metadata_version = metadata.get_metadata(task['id'],
+ task['token'])
command_dispatcher = CommandDispatcher('e' + task['id'], mq,
task['token'],
task['tenant_id'],
reporter)
+
workflows = []
- for path in glob.glob("data/workflows/*.xml"):
+ config = Config()
+ for path in glob.glob(
+ '{0}/workflows/*.xml'.format(metadata_version)):
log.debug('Loading XML {0}'.format(path))
workflow = Workflow(path, task, command_dispatcher, config,
- reporter)
+ reporter, metadata_version)
workflows.append(workflow)
stop = False
@@ -131,9 +136,11 @@ class ConductorWorkflowService(service.Service):
if stop:
log.info("Workflow stopped by 'stop' command")
do_ack = True
+ metadata.release(task['id'])
except Exception as ex:
log.exception(ex)
- log.debug("Non-processable message detected, will ack message")
+ log.debug("Non-processable message detected, "
+ "will ack message")
do_ack = True
finally:
if do_ack:
diff --git a/muranoconductor/cloud_formation.py b/muranoconductor/cloud_formation.py
index 0bef346..0ea07c2 100644
--- a/muranoconductor/cloud_formation.py
+++ b/muranoconductor/cloud_formation.py
@@ -14,14 +14,14 @@
# limitations under the License.
import base64
-import config
+from os.path import basename
import random
import string
import time
import datetime
-
import xml_code_engine
from openstack.common import log as logging
+from muranoconductor import config as cfg
log = logging.getLogger(__name__)
@@ -29,6 +29,7 @@ log = logging.getLogger(__name__)
def update_cf_stack(engine, context, body, template, result=None, error=None,
**kwargs):
command_dispatcher = context['/commandDispatcher']
+ metadata_id = context['/metadata_id']
def callback(result_value, error_result=None):
if result is not None:
@@ -60,7 +61,8 @@ def update_cf_stack(engine, context, body, template, result=None, error=None,
name='cf', command='CreateOrUpdate', template=template,
mappings=(kwargs.get('mappings') or {}),
arguments=(kwargs.get('arguments') or {}),
- callback=callback)
+ callback=callback,
+ metadata_id=metadata_id)
def delete_cf_stack(engine, context, body, **kwargs):
@@ -77,11 +79,13 @@ def delete_cf_stack(engine, context, body, **kwargs):
def prepare_user_data(context, hostname, service, unit,
template='Default', initFile='init.ps1', **kwargs):
- settings = config.CONF.rabbitmq
-
- with open('data/{0}'.format(initFile)) as init_script_file:
- with open('data/templates/agent-config/{0}.template'.format(
- template)) as template_file:
+ settings = cfg.CONF.rabbitmq
+ path_to_init_file = '{0}/{1}'.format(basename(cfg.CONF.init_scripts_dir),
+ initFile)
+ with open(path_to_init_file) as init_script_file:
+ with open('{0}/{1}.template'.format(
+ basename(cfg.CONF.agent_config_dir), template)
+ ) as template_file:
init_script = init_script_file.read()
template_data = template_file.read()
@@ -108,7 +112,7 @@ def prepare_user_data(context, hostname, service, unit,
init_script = init_script.replace('%INTERNAL_HOSTNAME%', hostname)
init_script = init_script.replace(
'%MURANO_SERVER_ADDRESS%',
- config.CONF.file_server or settings.host)
+ cfg.CONF.file_server or settings.host)
init_script = init_script.replace(
'%CA_ROOT_CERT_BASE64%',
@@ -124,7 +128,7 @@ def set_config_params(template_data, replacements):
def get_ca_certificate():
- ca_file = (config.CONF.rabbitmq.ca_certs or '').strip()
+ ca_file = (cfg.CONF.rabbitmq.ca_certs or '').strip()
if not ca_file:
return ''
with open(ca_file) as stream:
diff --git a/muranoconductor/cmd/run.py b/muranoconductor/cmd/run.py
index d653134..df18890 100644
--- a/muranoconductor/cmd/run.py
+++ b/muranoconductor/cmd/run.py
@@ -31,12 +31,13 @@ from muranoconductor import config
from muranoconductor.openstack.common import log
from muranoconductor.openstack.common import service
from muranoconductor.app import ConductorWorkflowService
+from muranoconductor import metadata
def main():
try:
config.parse_args()
- os.chdir(config.CONF.data_dir)
+ metadata.prepare(config.CONF.data_dir)
log.setup('conductor')
launcher = service.ServiceLauncher()
launcher.launch_service(ConductorWorkflowService())
diff --git a/muranoconductor/commands/cloud_formation.py b/muranoconductor/commands/cloud_formation.py
index a7d58e1..24d13dd 100644
--- a/muranoconductor/commands/cloud_formation.py
+++ b/muranoconductor/commands/cloud_formation.py
@@ -77,12 +77,16 @@ class HeatExecutor(CommandBase):
kwargs.get('mappings') or {}),
muranoconductor.helpers.str2unicode(
kwargs.get('arguments') or {}),
- callback)
+ callback,
+ kwargs['metadata_id'])
elif command == 'Delete':
return self._execute_delete(callback)
- def _execute_create_update(self, template, mappings, arguments, callback):
- with open('data/templates/cf/%s.template' % template) as template_file:
+ def _execute_create_update(self, template, mappings,
+ arguments, callback, metadata_id):
+ template_path = '{0}/templates/cf/{1}.template'.format(metadata_id,
+ template)
+ with open(template_path) as template_file:
template_data = template_file.read()
template_data = muranoconductor.helpers.transform_json(
diff --git a/muranoconductor/commands/dispatcher.py b/muranoconductor/commands/dispatcher.py
index 5850f40..bb98520 100644
--- a/muranoconductor/commands/dispatcher.py
+++ b/muranoconductor/commands/dispatcher.py
@@ -19,12 +19,13 @@ import vm_agent
class CommandDispatcher(command.CommandBase):
- def __init__(self, environment, rmqclient, token, tenant_id, reporter):
+ def __init__(self, environment, rmqclient, token,
+ tenant_id, reporter):
self._command_map = {
'cf': cloud_formation.HeatExecutor(environment, token, tenant_id,
reporter),
- 'agent': vm_agent.VmAgentExecutor(
- environment, rmqclient, reporter)
+ 'agent': vm_agent.VmAgentExecutor(environment, rmqclient,
+ reporter)
}
def execute(self, name, **kwargs):
diff --git a/muranoconductor/commands/vm_agent.py b/muranoconductor/commands/vm_agent.py
index 13f8164..4720b62 100644
--- a/muranoconductor/commands/vm_agent.py
+++ b/muranoconductor/commands/vm_agent.py
@@ -21,9 +21,11 @@ class VmAgentExecutor(CommandBase):
self._reporter = reporter
rmqclient.declare(self._results_queue)
- def execute(self, template, mappings, unit, service, callback,
+ def execute(self, template, mappings, unit, service, callback, metadata_id,
timeout=None):
- template_path = 'data/templates/agent/%s.template' % template
+ template_path = '{0}/templates/agent/{1}.template'.format(metadata_id,
+ template)
+
#with open(template_path) as t_file:
# template_data = t_file.read()
#
diff --git a/muranoconductor/config.py b/muranoconductor/config.py
index a40727d..eadaeeb 100644
--- a/muranoconductor/config.py
+++ b/muranoconductor/config.py
@@ -34,6 +34,12 @@ paste_deploy_opts = [
cfg.StrOpt('config_file'),
]
+directories = [
+ cfg.StrOpt('data_dir', default='program_data'),
+ cfg.StrOpt('init_scripts_dir', default='./etc/init-scripts'),
+ cfg.StrOpt('agent_config_dir', default='./etc/agent-config'),
+]
+
rabbit_opts = [
cfg.StrOpt('host', default='localhost'),
cfg.IntOpt('port', default=5672),
@@ -65,8 +71,10 @@ CONF.register_opts(paste_deploy_opts, group='paste_deploy')
CONF.register_opts(rabbit_opts, group='rabbitmq')
CONF.register_opts(heat_opts, group='heat')
CONF.register_opts(keystone_opts, group='keystone')
+CONF.register_opts(directories)
CONF.register_opt(cfg.StrOpt('file_server'))
-CONF.register_cli_opt(cfg.StrOpt('data_dir', default='./'))
+CONF.register_cli_opt(cfg.StrOpt('murano_metadata_url'))
+
CONF.register_opt(cfg.IntOpt('max_environments', default=20))
diff --git a/muranoconductor/metadata.py b/muranoconductor/metadata.py
new file mode 100644
index 0000000..bd8ac12
--- /dev/null
+++ b/muranoconductor/metadata.py
@@ -0,0 +1,162 @@
+# Copyright (c) 2013 Mirantis Inc.
+#
+# 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 sys
+import tarfile
+import shutil
+import tempfile
+import hashlib
+from glob import glob
+from metadataclient.common.exceptions import CommunicationError
+from muranoconductor import config
+from metadataclient.v1.client import Client
+import os
+from openstack.common import log as logging
+
+CHUNK_SIZE = 1 << 20 # 1MB
+
+log = logging.getLogger(__name__)
+CONF = config.CONF
+
+
+class MetadataException(BaseException):
+ # Inherited not from Exception in purpose:
+ # On this exception ack message would not be sent
+ pass
+
+
+def _unpack_data_archive(task_id, hash):
+ archive_name = hash + '.tar.gz'
+ if not tarfile.is_tarfile(archive_name):
+ raise MetadataException('Received invalid file {0} from Metadata '
+ 'Repository'.format(hash))
+ dst_dir = task_id
+ if not os.path.exists(dst_dir):
+ os.mkdir(dst_dir)
+ with tarfile.open(archive_name, 'r:gz') as tar:
+ tar.extractall(path=dst_dir)
+ return dst_dir
+
+
+def get_endpoint():
+ endpoint = CONF.murano_metadata_url
+
+ if not endpoint:
+ #TODO: add keystone catalog lookup
+ pass
+ return endpoint
+
+
+def metadataclient(token_id):
+ endpoint = get_endpoint()
+ return Client(endpoint=endpoint, token=token_id)
+
+
+def get_metadata(task_id, token_id):
+ hash = _check_existing_hash()
+ try:
+ log.debug('Retrieving metadata from Murano Metadata Repository')
+ resp, body_iter = metadataclient(token_id).\
+ metadata_client.get_conductor_data(hash)
+ except CommunicationError as e:
+ if hash:
+ log.warning('Metadata update failed: '
+ 'Unable to connect Metadata Repository due to {0}. '
+ 'Using existing version of metadata'.format(e))
+ else:
+ log.exception(e)
+ exc_type, exc_value, exc_traceback = sys.exc_info()
+ raise MetadataException('Unable to get data '
+ 'from Metadata Repository due to {0}: '
+ '{1}'.format(exc_type.__name__, exc_value))
+
+ else:
+ if resp.status == 304:
+ log.debug('Metadata unmodified. Using existing archive.')
+
+ elif resp.status == 200:
+ with tempfile.NamedTemporaryFile(delete=False) as archive:
+ for chunk in body_iter:
+ archive.write(chunk)
+ hash = _get_hash(archive.name)
+ shutil.move(archive.name, hash + '.tar.gz')
+ else:
+ msg = 'Metadata update failed: ' \
+ 'Got {0} status in response.'.format(resp.status)
+ if hash:
+ log.warning(msg + ' Using existing version of metadata.')
+ else:
+ raise MetadataException(msg)
+ return _unpack_data_archive(task_id, hash)
+
+
+def release(folder):
+ log.debug('Deleting metadata folder {0}'.format(folder))
+ try:
+ shutil.rmtree(folder)
+ except Exception as e:
+ log.exception('Unable to delete folder {0} with '
+ 'task metadata due to {1}'.format(folder, e))
+
+
+def prepare(data_dir):
+ if not os.path.exists(data_dir):
+ os.makedirs(data_dir)
+ init_scripts_dst = os.path.join(data_dir,
+ os.path.basename(CONF.init_scripts_dir))
+ if not os.path.exists(init_scripts_dst):
+ shutil.copytree(CONF.init_scripts_dir, init_scripts_dst)
+ agent_config_dst = os.path.join(data_dir,
+ os.path.basename(CONF.agent_config_dir))
+ if not os.path.exists(agent_config_dst):
+ shutil.copytree(CONF.agent_config_dir, agent_config_dst)
+ os.chdir(data_dir)
+
+
+def _get_hash(archive_path):
+ """Calculate SHA1-hash of archive file.
+
+ SHA-1 take a bit more time than MD5 (see http://tinyurl.com/kpj5jy7),
+ but is more secure.
+ """
+ if os.path.exists(archive_path):
+ sha1 = hashlib.sha1()
+ with open(archive_path) as f:
+ buf = f.read(CHUNK_SIZE)
+ while buf:
+ sha1.update(buf)
+ buf = f.read(CHUNK_SIZE)
+ hsum = sha1.hexdigest()
+ log.debug("Archive '{0}' has hash-sum {1}".format(archive_path, hsum))
+ return hsum
+ else:
+ log.info("Archive '{0}' doesn't exist, no hash to calculate".format(
+ archive_path))
+ return None
+
+
+def _check_existing_hash():
+ hash_archives = glob('*.tar.gz')
+ if not hash_archives:
+ hash = None
+ else:
+ if len(hash_archives) > 1:
+ log.warning('There are to metadata archive. Deleting them both')
+ for item in hash_archives:
+ os.remove(item)
+ hash = None
+ else:
+ file_name, extension = hash_archives[0].split('.', 1)
+ hash = file_name
+ return hash
diff --git a/muranoconductor/vm_agent.py b/muranoconductor/vm_agent.py
index d4d4755..6545e0e 100644
--- a/muranoconductor/vm_agent.py
+++ b/muranoconductor/vm_agent.py
@@ -74,6 +74,7 @@ def _extract_v2_results(result_value, ok, errors):
def send_command(engine, context, body, template, service, unit,
mappings=None, result=None, error=None, timeout=None,
osVersion=None, **kwargs):
+ metadata_id = context['/metadata_id']
if not mappings:
mappings = {}
if osVersion:
@@ -112,8 +113,14 @@ def send_command(engine, context, body, template, service, unit,
raise UnhandledAgentException(errors)
command_dispatcher.execute(
- name='agent', template=template, mappings=mappings,
- unit=unit, service=service, callback=callback, timeout=timeout)
+ name='agent',
+ template=template,
+ mappings=mappings,
+ unit=unit,
+ service=service,
+ callback=callback,
+ timeout=timeout,
+ metadata_id=metadata_id)
def _get_array_item(array, index):
@@ -127,7 +134,7 @@ def _get_exception_info(data):
'message': _get_array_item(data, 1),
'command': _get_array_item(data, 2),
'details': _get_array_item(data, 3),
- 'timestamp': datetime.datetime.now().isoformat()
+ 'timestamp': datetime.datetime.now().isoformat()
}
xml_code_engine.XmlCodeEngine.register_function(send_command, "send-command")
diff --git a/muranoconductor/workflow.py b/muranoconductor/workflow.py
index baf7a1b..5b441b4 100644
--- a/muranoconductor/workflow.py
+++ b/muranoconductor/workflow.py
@@ -28,7 +28,8 @@ object_id = id
class Workflow(object):
- def __init__(self, filename, data, command_dispatcher, config, reporter):
+ def __init__(self, filename, data, command_dispatcher,
+ config, reporter, metadata_id):
self._data = data
self._engine = xml_code_engine.XmlCodeEngine()
with open(filename) as xml:
@@ -40,6 +41,7 @@ class Workflow(object):
# format: (rule-id, entity-id) => True for auto-reset bans,
# False for permanent bans
self._blacklist = {}
+ self._metadata_id = metadata_id
def execute(self):
context = function_context.Context()
@@ -48,6 +50,7 @@ class Workflow(object):
context['/config'] = self._config
context['/reporter'] = self._reporter
context['/__blacklist'] = self._blacklist
+ context['/metadata_id'] = self._metadata_id
return self._engine.execute(context)
def prepare(self):
diff --git a/requirements.txt b/requirements.txt
index e83ee45..6b28786 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -13,4 +13,4 @@ oslo.config>=1.2.0
deep
murano-common>=0.2.2
PyYAML>=3.1.0
-
+murano-metadataclient==0.4.a13.gd65dfd2
diff --git a/tests/conductor/test_heat_commands.py b/tests/conductor/test_heat_commands.py
index b0f236d..6d083ac 100644
--- a/tests/conductor/test_heat_commands.py
+++ b/tests/conductor/test_heat_commands.py
@@ -31,8 +31,10 @@ class TestHeatExecutor(unittest.TestCase):
"$key": "$value"
}
}
+ self.metadata_id = 'b5bbea94023083e1ee06a52af5663b15c1fb1b7c'
self.mfs.add_entries({
- './data/templates/cf/test.template': json.dumps(template)})
+ './{0}/templates/cf/test.template'.format(self.metadata_id):
+ json.dumps(template)})
def tearDown(self):
mockfs.restore_builtins()
@@ -66,7 +68,8 @@ class TestHeatExecutor(unittest.TestCase):
arguments={
'arg1': 'arg1Value',
'arg2': 'arg2Value'},
- callback=callback)
+ callback=callback,
+ metadata_id=self.metadata_id)
heat_mock().stacks.get().stack_status = 'CREATE_COMPLETE'
heat_mock().stacks.template = mock.MagicMock(
@@ -107,7 +110,8 @@ class TestHeatExecutor(unittest.TestCase):
arguments={
'arg1': 'arg1Value',
'arg2': 'arg2Value'},
- callback=callback)
+ callback=callback,
+ metadata_id=self.metadata_id)
get_mock = heat_mock().stacks.get()
get_mock.stack_name = 'stack'
diff --git a/tests/conductor/test_vm_agent.py b/tests/conductor/test_vm_agent.py
index 05eb69f..ca43292 100644
--- a/tests/conductor/test_vm_agent.py
+++ b/tests/conductor/test_vm_agent.py
@@ -36,16 +36,23 @@ class TestVmAgent(unittest.TestCase):
}],
"RebootOnCompletion": 0
}
+ self.metadata_id = 'a8571e3b1ba6b33f6c7dbe0f81217c5070377abe'
self.mfs.add_entries({
- './data/templates/agent/test.template':
+ './a8571e3b1ba6b33f6c7dbe0f81217c5070377abe/'
+ 'templates/agent/test.template':
json.dumps(self.template),
- './data/templates/agent/scripts/Get-DnsListeningIpAddress.ps1':
+
+ './a8571e3b1ba6b33f6c7dbe0f81217c5070377abe/'
+ 'templates/agent/scripts/Get-DnsListeningIpAddress.ps1':
'function GetDNSip(){\ntest\n}\n',
- './data/templates/agent/scripts/Join-Domain.ps1':
+
+ './a8571e3b1ba6b33f6c7dbe0f81217c5070377abe/'
+ 'templates/agent/scripts/Join-Domain.ps1':
'function JoinDomain(){\ntest\n}\n',
})
- self.template_path = './data/templates/agent/test.template'
+ self.template_path = './a8571e3b1ba6b33f6c7dbe0f81217c5070377abe/' \
+ 'templates/agent/test.template'
def test_script_encode(self):
stack = mock.MagicMock()
diff --git a/tests/conductor/test_workflow.py b/tests/conductor/test_workflow.py
index 406d3d1..9d57296 100644
--- a/tests/conductor/test_workflow.py
+++ b/tests/conductor/test_workflow.py
@@ -37,6 +37,7 @@ class TestWorkflow(unittest.TestCase):
self.mfs = mockfs.replace_builtins()
self.model = json.loads(load_sample('objectModel1.json'))
self.original_model = json.loads(load_sample('objectModel1.json'))
+ self.metadata_id = 'b5bbea94023083e1ee06a52af5663b15c1fb1b7c'
def tearDown(self):
mockfs.restore_builtins()
@@ -45,7 +46,8 @@ class TestWorkflow(unittest.TestCase):
self.mfs.add_entries({'test': xml})
stub = mock.MagicMock()
stub.side_effect = RuntimeError
- workflow = Workflow('test', self.model, stub, stub, stub)
+ workflow = Workflow('test', self.model, stub,
+ stub, stub, self.metadata_id)
workflow.execute()
def test_empty_workflow_leaves_object_model_unchanged(self):