Enable getting data from metadatarepository
Modify unit tests Change-Id: I75a71b48475b75ca6603756c33fad3742fea2900
This commit is contained in:
parent
ce869822e4
commit
bf2cc47a7e
36
etc/agent-config/Default.template
Normal file
36
etc/agent-config/Default.template
Normal file
@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<configuration>
|
||||
<configSections>
|
||||
<section name="nlog" type="NLog.Config.ConfigSectionHandler, NLog"/>
|
||||
</configSections>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
|
||||
</startup>
|
||||
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<targets>
|
||||
<target name="file" xsi:type="File" fileName="${basedir}/log.txt"
|
||||
layout="${date} ${level}: <${logger:shortName=true}> ${message} ${exception:format=tostring}"/>
|
||||
</targets>
|
||||
|
||||
<rules>
|
||||
<logger name="*" minlevel="Debug" writeTo="file" />
|
||||
</rules>
|
||||
</nlog>
|
||||
<appSettings>
|
||||
<add key="rabbitmq.host" value="%RABBITMQ_HOST%"/>
|
||||
<add key="rabbitmq.port" value="%RABBITMQ_PORT%"/>
|
||||
<add key="rabbitmq.user" value="%RABBITMQ_USER%"/>
|
||||
<add key="rabbitmq.password" value="%RABBITMQ_PASSWORD%"/>
|
||||
<add key="rabbitmq.vhost" value="%RABBITMQ_VHOST%"/>
|
||||
<add key="rabbitmq.inputQueue" value="%RABBITMQ_INPUT_QUEUE%"/>
|
||||
<add key="rabbitmq.resultExchange" value=""/>
|
||||
<add key="rabbitmq.resultRoutingKey" value="%RESULT_QUEUE%"/>
|
||||
<add key="rabbitmq.durableMessages" value="true"/>
|
||||
|
||||
<add key="rabbitmq.ssl" value="%RABBITMQ_SSL%"/>
|
||||
<add key="rabbitmq.allowInvalidCA" value="true"/>
|
||||
<add key="rabbitmq.sslServerName" value=""/>
|
||||
|
||||
</appSettings>
|
||||
</configuration>
|
8
etc/agent-config/Demo.template
Normal file
8
etc/agent-config/Demo.template
Normal file
@ -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%"
|
35
etc/agent-config/Linux.template
Normal file
35
etc/agent-config/Linux.template
Normal file
@ -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%
|
@ -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
|
||||
|
||||
|
11
etc/init-scripts/demo_init.sh
Normal file
11
etc/init-scripts/demo_init.sh
Normal file
@ -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
|
68
etc/init-scripts/init.ps1
Normal file
68
etc/init-scripts/init.ps1
Normal file
@ -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 '<exception>'
|
||||
Write-LogError $_ -EntireObject
|
||||
Write-LogError '</exception>'
|
||||
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'
|
||||
}
|
6
etc/init-scripts/linux_init.sh
Normal file
6
etc/init-scripts/linux_init.sh
Normal file
@ -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
|
@ -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':
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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())
|
||||
|
@ -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(
|
||||
|
@ -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):
|
||||
|
@ -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()
|
||||
#
|
||||
|
@ -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))
|
||||
|
||||
|
162
muranoconductor/metadata.py
Normal file
162
muranoconductor/metadata.py
Normal file
@ -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
|
@ -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")
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
@ -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'
|
||||
|
@ -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()
|
||||
|
@ -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):
|
||||
|
Loading…
x
Reference in New Issue
Block a user