Fixes silent deletion of environments
Now environment deletion is done as a regular deployment that can fail. Environments that are deleted, but deletion process has failed remain in database and shown in dashboard with status 'delete failure'. Environments that are being deleted has status 'deleting' and do not disappear before they really got deleted on engine side Also improved status reporting for environments. Now it also reports status of last deployment - 'deploy failure', 'delete failure' P.S. Functional tests were slightly refactored and fixed to reflect changes in deletion logic Change-Id: I05625dd71f7ca9559bb88319b26b122214f15019 Closes-Bug: #1325101
This commit is contained in:
parent
2309247b24
commit
56b2d5df27
3
.gitignore
vendored
3
.gitignore
vendored
@ -27,3 +27,6 @@ cover
|
||||
|
||||
#Autogenerated Documentation
|
||||
doc/source/api
|
||||
|
||||
#Config file for functional tests
|
||||
murano/tests/functional/engine/config.conf
|
||||
|
@ -16,6 +16,7 @@ from sqlalchemy import desc
|
||||
from webob import exc
|
||||
|
||||
from murano.api.v1 import request_statistics
|
||||
from murano.api.v1 import sessions
|
||||
from murano.common import policy
|
||||
from murano.common import utils
|
||||
from murano.db import models
|
||||
@ -123,22 +124,11 @@ class Controller(object):
|
||||
LOG.debug('Environments:Delete <Id: {0}>'.format(environment_id))
|
||||
target = {"environment_id": environment_id}
|
||||
policy.check('delete_environment', request.context, target)
|
||||
|
||||
unit = db_session.get_session()
|
||||
environment = unit.query(models.Environment).get(environment_id)
|
||||
|
||||
if environment is None:
|
||||
LOG.info(_('Environment <EnvId {0}> '
|
||||
'is not found').format(environment_id))
|
||||
raise exc.HTTPNotFound
|
||||
|
||||
if environment.tenant_id != request.context.tenant:
|
||||
LOG.info(_('User is not authorized to access '
|
||||
'this tenant resources.'))
|
||||
raise exc.HTTPUnauthorized
|
||||
|
||||
envs.EnvironmentServices.delete(environment_id,
|
||||
request.context.auth_token)
|
||||
sessions_controller = sessions.Controller()
|
||||
session = sessions_controller.configure(request, environment_id)
|
||||
session_id = session['id']
|
||||
envs.EnvironmentServices.delete(environment_id, session_id)
|
||||
sessions_controller.deploy(request, environment_id, session_id)
|
||||
|
||||
@request_statistics.stats_count(API_NAME, 'LastStatus')
|
||||
def last(self, request, environment_id):
|
||||
@ -150,7 +140,7 @@ class Controller(object):
|
||||
session_id)
|
||||
session = db_session.get_session()
|
||||
result = {}
|
||||
for service in services:
|
||||
for service in services or []:
|
||||
service_id = service['?']['id']
|
||||
entity_ids = utils.build_entity_map(service).keys()
|
||||
last_status = session.query(models.Status). \
|
||||
|
@ -49,7 +49,8 @@ class Controller(object):
|
||||
|
||||
# no new session can be opened if environment has deploying status
|
||||
env_status = envs.EnvironmentServices.get_status(environment_id)
|
||||
if env_status == envs.EnvironmentStatus.deploying:
|
||||
if env_status in (envs.EnvironmentStatus.DEPLOYING,
|
||||
envs.EnvironmentStatus.DELETING):
|
||||
LOG.info(_('Could not open session for environment <EnvId: {0}>,'
|
||||
'environment has deploying '
|
||||
'status.').format(environment_id))
|
||||
@ -113,7 +114,7 @@ class Controller(object):
|
||||
'<SessionId {1}>.').format(user_id, session_id))
|
||||
raise exc.HTTPUnauthorized()
|
||||
|
||||
if session.state == sessions.SessionState.deploying:
|
||||
if session.state == sessions.SessionState.DEPLOYING:
|
||||
LOG.error(_('Session <SessionId: {0}> is in deploying state and '
|
||||
'could not be deleted').format(session_id))
|
||||
raise exc.HTTPForbidden()
|
||||
@ -145,7 +146,7 @@ class Controller(object):
|
||||
'is invalid').format(session_id))
|
||||
raise exc.HTTPForbidden()
|
||||
|
||||
if session.state != sessions.SessionState.open:
|
||||
if session.state != sessions.SessionState.OPENED:
|
||||
LOG.error(_('Session <SessionId {0}> is already deployed or '
|
||||
'deployment is in progress').format(session_id))
|
||||
raise exc.HTTPForbidden()
|
||||
|
@ -48,21 +48,19 @@ class TaskProcessingEndpoint(object):
|
||||
LOG.info(_('Starting processing task: {task_desc}').format(
|
||||
task_desc=anyjson.dumps(s_task)))
|
||||
|
||||
result = task['model']
|
||||
try:
|
||||
task_executor = TaskExecutor(task)
|
||||
result = task_executor.execute()
|
||||
rpc.api().process_result(result)
|
||||
except Exception as e:
|
||||
# TODO(gokrokve) report error here
|
||||
# TODO(slagun) code below needs complete rewrite and redesign
|
||||
LOG.exception("Error during task execution for tenant %s",
|
||||
LOG.exception('Error during task execution for tenant %s',
|
||||
task['tenant_id'])
|
||||
if task['model']['Objects']:
|
||||
msg_env = Environment(task['model']['Objects']['?']['id'])
|
||||
reporter = status_reporter.StatusReporter()
|
||||
reporter.initialize(msg_env)
|
||||
reporter.report_error(msg_env, str(e))
|
||||
rpc.api().process_result(task['model'])
|
||||
msg_env = Environment(task['id'])
|
||||
reporter = status_reporter.StatusReporter()
|
||||
reporter.initialize(msg_env)
|
||||
reporter.report_error(msg_env, str(e))
|
||||
finally:
|
||||
rpc.api().process_result(result, task['id'])
|
||||
|
||||
|
||||
def _prepare_rpc_service(server_id):
|
||||
|
@ -26,8 +26,9 @@ class ApiClient(object):
|
||||
client_target = target.Target('murano', 'results')
|
||||
self._client = rpc.RPCClient(transport, client_target, timeout=15)
|
||||
|
||||
def process_result(self, result):
|
||||
return self._client.call({}, 'process_result', result=result)
|
||||
def process_result(self, result, environment_id):
|
||||
return self._client.call({}, 'process_result', result=result,
|
||||
environment_id=environment_id)
|
||||
|
||||
|
||||
class EngineClient(object):
|
||||
|
@ -23,7 +23,9 @@ from sqlalchemy import desc
|
||||
from murano.common import config
|
||||
from murano.common.helpers import token_sanitizer
|
||||
from murano.db import models
|
||||
from murano.db.services import environments
|
||||
from murano.db.services import instances
|
||||
from murano.db.services import sessions
|
||||
from murano.db import session
|
||||
from murano.openstack.common.gettextutils import _ # noqa
|
||||
from murano.openstack.common import log as logging
|
||||
@ -38,39 +40,36 @@ LOG = logging.getLogger(__name__)
|
||||
|
||||
class ResultEndpoint(object):
|
||||
@staticmethod
|
||||
def process_result(context, result):
|
||||
def process_result(context, result, environment_id):
|
||||
secure_result = token_sanitizer.TokenSanitizer().sanitize(result)
|
||||
LOG.debug('Got result from orchestration '
|
||||
'engine:\n{0}'.format(secure_result))
|
||||
|
||||
if not result['Objects']:
|
||||
LOG.debug('Ignoring result for deleted environment')
|
||||
return
|
||||
|
||||
result_id = result['Objects']['?']['id']
|
||||
|
||||
unit = session.get_session()
|
||||
environment = unit.query(models.Environment).get(result_id)
|
||||
environment = unit.query(models.Environment).get(environment_id)
|
||||
|
||||
if not environment:
|
||||
LOG.warning(_('Environment result could not be handled, specified '
|
||||
'environment was not found in database'))
|
||||
return
|
||||
|
||||
if result['Objects'] is None and result.get('ObjectsCopy', {}) is None:
|
||||
environments.EnvironmentServices.remove(environment_id)
|
||||
return
|
||||
|
||||
environment.description = result
|
||||
environment.description['Objects']['services'] = \
|
||||
environment.description['Objects'].get('applications', [])
|
||||
del environment.description['Objects']['applications']
|
||||
environment.networking = result.get('networking', {})
|
||||
if environment.description['Objects'] is not None:
|
||||
environment.description['Objects']['services'] = \
|
||||
environment.description['Objects'].pop('applications', [])
|
||||
# environment.networking = result.get('networking', {})
|
||||
action_name = 'Deployment'
|
||||
deleted = False
|
||||
else:
|
||||
action_name = 'Deletion'
|
||||
deleted = True
|
||||
environment.version += 1
|
||||
environment.save(unit)
|
||||
|
||||
#close session
|
||||
conf_session = unit.query(models.Session).filter_by(
|
||||
**{'environment_id': environment.id, 'state': 'deploying'}).first()
|
||||
conf_session.state = 'deployed'
|
||||
conf_session.save(unit)
|
||||
|
||||
#close deployment
|
||||
deployment = get_last_deployment(unit, environment.id)
|
||||
deployment.finished = timeutils.utcnow()
|
||||
@ -80,7 +79,7 @@ class ResultEndpoint(object):
|
||||
num_warnings = unit.query(models.Status)\
|
||||
.filter_by(level='warning', deployment_id=deployment.id).count()
|
||||
|
||||
final_status_text = "Deployment finished"
|
||||
final_status_text = action_name + ' finished'
|
||||
if num_errors:
|
||||
final_status_text += " with errors"
|
||||
|
||||
@ -94,6 +93,18 @@ class ResultEndpoint(object):
|
||||
deployment.statuses.append(status)
|
||||
deployment.save(unit)
|
||||
|
||||
#close session
|
||||
conf_session = unit.query(models.Session).filter_by(
|
||||
**{'environment_id': environment.id,
|
||||
'state': 'deploying' if not deleted else 'deleting'}).first()
|
||||
if num_errors > 0:
|
||||
conf_session.state = \
|
||||
sessions.SessionState.DELETE_FAILURE if deleted else \
|
||||
sessions.SessionState.DEPLOY_FAILURE
|
||||
else:
|
||||
conf_session.state = sessions.SessionState.DEPLOYED
|
||||
conf_session.save(unit)
|
||||
|
||||
|
||||
def notification_endpoint_wrapper(priority='info'):
|
||||
def wrapper(func):
|
||||
|
@ -12,6 +12,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
import types
|
||||
from webob import exc
|
||||
|
||||
from murano.common import utils
|
||||
from murano.db.services import environments as envs
|
||||
@ -46,6 +47,9 @@ class CoreServices(object):
|
||||
|
||||
env_description = get_description(environment_id, session_id)
|
||||
|
||||
if env_description is None:
|
||||
return None
|
||||
|
||||
if not 'services' in env_description:
|
||||
return []
|
||||
|
||||
@ -65,6 +69,8 @@ class CoreServices(object):
|
||||
save_environment_description
|
||||
|
||||
env_description = get_description(environment_id, session_id)
|
||||
if env_description is None:
|
||||
raise exc.HTTPMethodNotAllowed
|
||||
if not 'services' in env_description:
|
||||
env_description['services'] = []
|
||||
|
||||
|
@ -14,7 +14,6 @@
|
||||
|
||||
import collections
|
||||
|
||||
from murano.common import rpc
|
||||
from murano.common import uuidutils
|
||||
|
||||
from murano.db import models
|
||||
@ -23,9 +22,16 @@ from murano.db import session as db_session
|
||||
|
||||
|
||||
EnvironmentStatus = collections.namedtuple('EnvironmentStatus', [
|
||||
'ready', 'pending', 'deploying'
|
||||
'READY', 'PENDING', 'DEPLOYING', 'DEPLOY_FAILURE', 'DELETING',
|
||||
'DELETE_FAILURE'
|
||||
])(
|
||||
ready='ready', pending='pending', deploying='deploying'
|
||||
READY='ready',
|
||||
PENDING='pending',
|
||||
DEPLOYING='deploying',
|
||||
DEPLOY_FAILURE='deploy failure',
|
||||
DELETING='deleting',
|
||||
DELETE_FAILURE='delete failure'
|
||||
|
||||
)
|
||||
|
||||
DEFAULT_NETWORKS = {
|
||||
@ -54,31 +60,37 @@ class EnvironmentServices(object):
|
||||
@staticmethod
|
||||
def get_status(environment_id):
|
||||
"""
|
||||
Environment can have one of three distinguished statuses:
|
||||
Environment can have one of the following statuses:
|
||||
|
||||
- Deploying: there is at least one session with status `deploying`;
|
||||
- Pending: there is at least one session with status `open`;
|
||||
- Ready: there is no sessions in status `deploying` or `open`.
|
||||
- deploying: there is ongoing deployment for environment
|
||||
- deleting: environment is currently being deleted
|
||||
- deploy failure: last deployment session has failed
|
||||
- delete failure: last delete session has failed
|
||||
- pending: there is at least one session with status `open` and no
|
||||
errors in previous sessions
|
||||
- ready: there are no sessions for environment
|
||||
|
||||
:param environment_id: Id of environment for which we checking status.
|
||||
:return: Environment status
|
||||
"""
|
||||
#Deploying: there is at least one valid session with status `deploying`
|
||||
deploying = sessions.SessionServices.get_sessions(
|
||||
environment_id,
|
||||
sessions.SessionState.deploying)
|
||||
if len(deploying) > 0:
|
||||
return 'deploying'
|
||||
session_list = sessions.SessionServices.get_sessions(environment_id)
|
||||
has_opened = False
|
||||
for session in session_list:
|
||||
if session.state == sessions.SessionState.DEPLOYING:
|
||||
return EnvironmentStatus.DEPLOYING
|
||||
elif session.state == sessions.SessionState.DELETING:
|
||||
return EnvironmentStatus.DELETING
|
||||
elif session.state == sessions.SessionState.DEPLOY_FAILURE:
|
||||
return EnvironmentStatus.DEPLOY_FAILURE
|
||||
elif session.state == sessions.SessionState.DELETE_FAILURE:
|
||||
return EnvironmentStatus.DELETE_FAILURE
|
||||
elif session.state == sessions.SessionState.OPENED:
|
||||
has_opened = True
|
||||
if has_opened:
|
||||
return EnvironmentStatus.PENDING
|
||||
|
||||
#Pending: there is at least one valid session with status `open`;
|
||||
open = sessions.SessionServices.get_sessions(
|
||||
environment_id,
|
||||
sessions.SessionState.open)
|
||||
if len(open) > 0:
|
||||
return 'pending'
|
||||
|
||||
#Ready: there are no sessions in status `deploying` or `open`
|
||||
return 'ready'
|
||||
return EnvironmentStatus.READY
|
||||
|
||||
@staticmethod
|
||||
def create(environment_params, tenant_id):
|
||||
@ -118,30 +130,27 @@ class EnvironmentServices(object):
|
||||
return environment
|
||||
|
||||
@staticmethod
|
||||
def delete(environment_id, token):
|
||||
def delete(environment_id, session_id):
|
||||
"""
|
||||
Deletes environment and notify orchestration engine about deletion
|
||||
|
||||
:param environment_id: Environment that is going to be deleted
|
||||
:param token: OpenStack auth token
|
||||
"""
|
||||
|
||||
env_description = EnvironmentServices.get_environment_description(
|
||||
environment_id, session_id, False)
|
||||
env_description['Objects'] = None
|
||||
EnvironmentServices.save_environment_description(
|
||||
session_id, env_description, False)
|
||||
|
||||
@staticmethod
|
||||
def remove(environment_id):
|
||||
unit = db_session.get_session()
|
||||
environment = unit.query(models.Environment).get(environment_id)
|
||||
|
||||
#preparing data for removal from conductor
|
||||
env = environment.description
|
||||
env['Objects'] = None
|
||||
|
||||
data = {
|
||||
'model': env,
|
||||
'token': token,
|
||||
'tenant_id': environment.tenant_id
|
||||
}
|
||||
|
||||
rpc.engine().handle_task(data)
|
||||
|
||||
with unit.begin():
|
||||
unit.delete(environment)
|
||||
if environment:
|
||||
with unit.begin():
|
||||
unit.delete(environment)
|
||||
|
||||
@staticmethod
|
||||
def get_environment_description(environment_id, session_id=None,
|
||||
@ -162,7 +171,7 @@ class EnvironmentServices(object):
|
||||
if session_id:
|
||||
session = unit.query(models.Session).get(session_id)
|
||||
if sessions.SessionServices.validate(session):
|
||||
if session.state != sessions.SessionState.deployed:
|
||||
if session.state != sessions.SessionState.DEPLOYED:
|
||||
env_description = session.description
|
||||
else:
|
||||
env = unit.query(models.Environment)\
|
||||
|
@ -20,9 +20,15 @@ from murano.db import session as db_session
|
||||
|
||||
|
||||
SessionState = collections.namedtuple('SessionState', [
|
||||
'open', 'deploying', 'deployed'
|
||||
'OPENED', 'DEPLOYING', 'DEPLOYED', 'DEPLOY_FAILURE', 'DELETING',
|
||||
'DELETE_FAILURE'
|
||||
])(
|
||||
open='open', deploying='deploying', deployed='deployed'
|
||||
OPENED='opened',
|
||||
DEPLOYING='deploying',
|
||||
DEPLOYED='deployed',
|
||||
DEPLOY_FAILURE='deploy failure',
|
||||
DELETING='deleting',
|
||||
DELETE_FAILURE='delete failure'
|
||||
)
|
||||
|
||||
|
||||
@ -41,17 +47,18 @@ class SessionServices(object):
|
||||
unit = db_session.get_session()
|
||||
# Here we duplicate logic for reducing calls to database
|
||||
# Checks for validation is same as in validate.
|
||||
environment = unit.query(models.Environment).get(environment_id)
|
||||
|
||||
return unit.query(models.Session).filter(
|
||||
query = unit.query(models.Session).filter(
|
||||
#Get all session for this environment
|
||||
models.Session.environment_id == environment_id,
|
||||
#in this state, if state is not specified return in all states
|
||||
models.Session.state.in_(SessionState
|
||||
if state is None else [state]),
|
||||
#Only sessions with same version as current env version are valid
|
||||
models.Session.version == environment.version
|
||||
).all()
|
||||
)
|
||||
|
||||
if state:
|
||||
#in this state, if state is not specified return in all states
|
||||
query = query.filter(models.Session.state == state),
|
||||
|
||||
return query.order_by(models.Session.version.desc(),
|
||||
models.Session.updated.desc()).all()
|
||||
|
||||
@staticmethod
|
||||
def create(environment_id, user_id):
|
||||
@ -68,7 +75,7 @@ class SessionServices(object):
|
||||
session = models.Session()
|
||||
session.environment_id = environment.id
|
||||
session.user_id = user_id
|
||||
session.state = SessionState.open
|
||||
session.state = SessionState.OPENED
|
||||
# used for checking if other sessions was deployed before this one
|
||||
session.version = environment.version
|
||||
# all changes to environment is stored here, and translated to
|
||||
@ -101,9 +108,9 @@ class SessionServices(object):
|
||||
|
||||
#if other session is deploying now current session is invalid
|
||||
other_is_deploying = unit.query(models.Session).filter_by(
|
||||
environment_id=session.environment_id, state=SessionState.deploying
|
||||
environment_id=session.environment_id, state=SessionState.DEPLOYING
|
||||
).count() > 0
|
||||
if session.state == SessionState.open and other_is_deploying:
|
||||
if session.state == SessionState.OPENED and other_is_deploying:
|
||||
return False
|
||||
|
||||
return True
|
||||
@ -123,32 +130,40 @@ class SessionServices(object):
|
||||
environment = unit.query(models.Environment).get(
|
||||
session.environment_id)
|
||||
|
||||
task = {
|
||||
'action': {
|
||||
deleted = session.description['Objects'] is None
|
||||
action = None
|
||||
if not deleted:
|
||||
action = {
|
||||
'object_id': environment.id,
|
||||
'method': 'deploy',
|
||||
'args': {}
|
||||
},
|
||||
}
|
||||
|
||||
task = {
|
||||
'action': action,
|
||||
'model': session.description,
|
||||
'token': token,
|
||||
'tenant_id': environment.tenant_id
|
||||
'tenant_id': environment.tenant_id,
|
||||
'id': environment.id
|
||||
}
|
||||
|
||||
task['model']['Objects']['?']['id'] = environment.id
|
||||
task['model']['Objects']['applications'] = \
|
||||
task['model']['Objects'].get('services', [])
|
||||
if not deleted:
|
||||
task['model']['Objects']['?']['id'] = environment.id
|
||||
task['model']['Objects']['applications'] = \
|
||||
task['model']['Objects'].get('services', [])
|
||||
|
||||
if 'services' in task['model']['Objects']:
|
||||
del task['model']['Objects']['services']
|
||||
if 'services' in task['model']['Objects']:
|
||||
del task['model']['Objects']['services']
|
||||
|
||||
session.state = SessionState.deploying
|
||||
session.state = SessionState.DELETING if deleted \
|
||||
else SessionState.DEPLOYING
|
||||
deployment = models.Deployment()
|
||||
deployment.environment_id = session.environment_id
|
||||
deployment.description = token_sanitizer.TokenSanitizer().sanitize(
|
||||
dict(session.description.get('Objects')))
|
||||
session.description.get('Objects'))
|
||||
status = models.Status()
|
||||
status.text = "Deployment scheduled"
|
||||
status.level = "info"
|
||||
status.text = ('Delete' if deleted else 'Deployment') + ' scheduled'
|
||||
status.level = 'info'
|
||||
deployment.statuses.append(status)
|
||||
|
||||
with unit.begin():
|
||||
|
@ -263,10 +263,7 @@ class MuranoDslExecutor(object):
|
||||
for obj in objects_to_clean:
|
||||
methods = obj.type.find_all_methods('destroy')
|
||||
for method in methods:
|
||||
try:
|
||||
method.invoke(self, obj, {})
|
||||
except Exception:
|
||||
pass
|
||||
method.invoke(self, obj, {})
|
||||
finally:
|
||||
self._object_store = backup
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
import json
|
||||
import os
|
||||
import requests
|
||||
import time
|
||||
import uuid
|
||||
|
||||
from tempest import clients
|
||||
@ -45,8 +46,24 @@ class MuranoClient(rest_client.RestClient):
|
||||
|
||||
return resp, json.loads(body)
|
||||
|
||||
def delete_environment(self, environment_id):
|
||||
return self.delete('v1/environments/{0}'.format(environment_id))
|
||||
def delete_environment(self, environment_id, timeout=180):
|
||||
def _is_exist():
|
||||
try:
|
||||
resp, _ = self.get('v1/environments/{0}'.format(
|
||||
environment_id))
|
||||
except exceptions.NotFound:
|
||||
return False
|
||||
return resp.status == 200
|
||||
|
||||
env_deleted = not _is_exist()
|
||||
self.delete('v1/environments/{0}'.format(environment_id))
|
||||
|
||||
start_time = time.time()
|
||||
while env_deleted is not True:
|
||||
if timeout and time.time() - start_time > timeout:
|
||||
raise Exception('Environment was not deleted')
|
||||
time.sleep(5)
|
||||
env_deleted = not _is_exist()
|
||||
|
||||
def update_environment(self, environment_id):
|
||||
post_body = '{"name": "%s"}' % ("changed-environment-name")
|
||||
|
@ -14,12 +14,14 @@
|
||||
|
||||
import json
|
||||
import os
|
||||
import requests
|
||||
import socket
|
||||
import time
|
||||
import urlparse
|
||||
import uuid
|
||||
|
||||
import requests
|
||||
import testresources
|
||||
import testtools
|
||||
import time
|
||||
import uuid
|
||||
|
||||
from heatclient import client as heatclient
|
||||
from keystoneclient.v2_0 import client as ksclient
|
||||
@ -31,66 +33,70 @@ CONF = cfg.cfg.CONF
|
||||
|
||||
|
||||
class Client(object):
|
||||
|
||||
def __init__(self, user, password, tenant, auth_url, murano_url):
|
||||
|
||||
self.auth = ksclient.Client(username=user, password=password,
|
||||
tenant_name=tenant, auth_url=auth_url)
|
||||
|
||||
self.endpoint = murano_url
|
||||
|
||||
self.auth = ksclient.Client(
|
||||
username=user, password=password,
|
||||
tenant_name=tenant, auth_url=auth_url
|
||||
)
|
||||
self.endpoint = urlparse.urljoin(murano_url, 'v1/')
|
||||
self.headers = {
|
||||
'X-Auth-Token': self.auth.auth_token,
|
||||
'Content-type': 'application/json'
|
||||
}
|
||||
|
||||
def get_url(self, url_part=None):
|
||||
return urlparse.urljoin(self.endpoint, url_part)
|
||||
|
||||
def create_environment(self, name):
|
||||
post_body = {'name': name}
|
||||
resp = requests.post(self.endpoint + 'environments',
|
||||
data=json.dumps(post_body),
|
||||
headers=self.headers)
|
||||
endpoint = self.get_url('environments')
|
||||
body = json.dumps({'name': name})
|
||||
|
||||
return resp.json()
|
||||
return requests.post(endpoint, data=body, headers=self.headers).json()
|
||||
|
||||
def delete_environment(self, environment_id):
|
||||
endpoint = '{0}environments/{1}'.format(self.endpoint, environment_id)
|
||||
return requests.delete(endpoint, headers=self.headers)
|
||||
def delete_environment(self, environment_id, timeout=180):
|
||||
endpoint = self.get_url('environments/%s' % environment_id)
|
||||
|
||||
def _is_exist():
|
||||
resp = requests.get(endpoint, headers=self.headers)
|
||||
return resp.status_code == requests.codes.ok
|
||||
|
||||
env_deleted = not _is_exist()
|
||||
requests.delete(endpoint, headers=self.headers)
|
||||
|
||||
start_time = time.time()
|
||||
while env_deleted is not True:
|
||||
if timeout and time.time() - start_time > timeout:
|
||||
raise Exception('Environment was not deleted')
|
||||
time.sleep(5)
|
||||
env_deleted = not _is_exist()
|
||||
|
||||
def get_environment(self, environment_id):
|
||||
return requests.get('{0}environments/{1}'.format(self.endpoint,
|
||||
environment_id),
|
||||
headers=self.headers).json()
|
||||
endpoint = self.get_url('environments/%s' % environment_id)
|
||||
return requests.get(endpoint, headers=self.headers).json()
|
||||
|
||||
def create_session(self, environment_id):
|
||||
post_body = None
|
||||
endpoint = '{0}environments/{1}/configure'.format(self.endpoint,
|
||||
environment_id)
|
||||
|
||||
return requests.post(endpoint, data=post_body,
|
||||
headers=self.headers).json()
|
||||
endpoint = self.get_url('environments/%s/configure' % environment_id)
|
||||
return requests.post(endpoint, headers=self.headers).json()
|
||||
|
||||
def deploy_session(self, environment_id, session_id):
|
||||
endpoint = '{0}environments/{1}/sessions/{2}/deploy'.format(
|
||||
self.endpoint, environment_id, session_id)
|
||||
|
||||
return requests.post(endpoint, data=None, headers=self.headers)
|
||||
endpoint = self.get_url('environments/{0}/sessions/{1}/deploy'.format(
|
||||
environment_id, session_id))
|
||||
return requests.post(endpoint, headers=self.headers)
|
||||
|
||||
def create_service(self, environment_id, session_id, json_data):
|
||||
endpoint = self.get_url('environments/%s/services' % environment_id)
|
||||
body = json.dumps(json_data)
|
||||
headers = self.headers.copy()
|
||||
headers.update({'x-configuration-session': session_id})
|
||||
headers['x-configuration-session'] = session_id
|
||||
|
||||
endpoint = '{0}environments/{1}/services'.format(self.endpoint,
|
||||
environment_id)
|
||||
|
||||
return requests.post(endpoint, data=json.dumps(json_data),
|
||||
headers=headers).json()
|
||||
return requests.post(endpoint, data=body, headers=headers).json()
|
||||
|
||||
def delete_service(self, environment_id, session_id, service_id):
|
||||
endpoint = self.get_url('environments/{0}/services/{1}'.format(
|
||||
environment_id, service_id
|
||||
))
|
||||
headers = self.headers.copy()
|
||||
headers.update({'x-configuration-session': session_id})
|
||||
|
||||
endpoint = '{0}environments/{1}/services/{2}'.format(
|
||||
self.endpoint, environment_id, service_id)
|
||||
headers['x-configuration-session'] = session_id
|
||||
|
||||
requests.delete(endpoint, headers=headers)
|
||||
|
||||
@ -98,7 +104,6 @@ class Client(object):
|
||||
environment = self.get_environment(environment_id)
|
||||
|
||||
start_time = time.time()
|
||||
|
||||
while environment['status'] != 'ready':
|
||||
if time.time() - start_time > 1200:
|
||||
return
|
||||
@ -112,11 +117,19 @@ class Client(object):
|
||||
for service in environment['services']]
|
||||
|
||||
def deployments_list(self, environment_id):
|
||||
endpoint = '{0}environments/{1}/deployments'.format(self.endpoint,
|
||||
environment_id)
|
||||
endpoint = self.get_url('environments/%s/deployments' % environment_id)
|
||||
response = requests.get(endpoint, headers=self.headers)
|
||||
return response.json()['deployments']
|
||||
|
||||
return requests.get(endpoint,
|
||||
headers=self.headers).json()['deployments']
|
||||
def upload_package(self, name, package, description):
|
||||
endpoint = self.get_url('catalog/packages')
|
||||
body = {'JsonString': json.dumps(description)}
|
||||
files = {name: open(package, 'rb')}
|
||||
headers = self.headers.copy()
|
||||
del headers['Content-type']
|
||||
|
||||
resp = requests.post(endpoint, data=body, files=files, headers=headers)
|
||||
return resp.json()['id']
|
||||
|
||||
|
||||
class MuranoBase(testtools.TestCase, testtools.testcase.WithAttributes,
|
||||
@ -143,49 +156,38 @@ class MuranoBase(testtools.TestCase, testtools.testcase.WithAttributes,
|
||||
cls.heat_client = heatclient.Client('1', endpoint=heat_url,
|
||||
token=cls.client.auth.auth_token)
|
||||
|
||||
cls.location = os.path.realpath(
|
||||
os.path.join(os.getcwd(), os.path.dirname(__file__)))
|
||||
cls.pkgs_path = os.path.abspath(os.path.join(
|
||||
os.path.dirname(__file__),
|
||||
os.path.pardir,
|
||||
'murano-app-incubator'
|
||||
))
|
||||
|
||||
cls.packages_path = '/'.join(cls.location.split('/')[:-1:])
|
||||
|
||||
def upload_package(package_name, body, app):
|
||||
#TODO(efedorova): Use muranoclient to upload packages
|
||||
files = {'%s' % package_name: open(
|
||||
os.path.join(cls.packages_path, app), 'rb')}
|
||||
|
||||
post_body = {'JsonString': json.dumps(body)}
|
||||
request_url = '{endpoint}{url}'.format(
|
||||
endpoint=CONF.murano.murano_url,
|
||||
url='catalog/packages')
|
||||
|
||||
headers = cls.client.headers.copy()
|
||||
del headers['Content-type']
|
||||
|
||||
return requests.post(request_url,
|
||||
files=files,
|
||||
data=post_body,
|
||||
headers=headers).json()['id']
|
||||
|
||||
cls.postgre_id = upload_package(
|
||||
cls.postgre_id = cls.client.upload_package(
|
||||
'PostgreSQL',
|
||||
{"categories": ["Databases"], "tags": ["tag"]},
|
||||
'murano-app-incubator/io.murano.apps.PostgreSql.zip')
|
||||
cls.apache_id = upload_package(
|
||||
os.path.join(cls.pkgs_path, 'io.murano.apps.PostgreSql.zip'),
|
||||
{'categories': ['Databases'], 'tags': ['tag']}
|
||||
)
|
||||
cls.apache_id = cls.client.upload_package(
|
||||
'Apache',
|
||||
{"categories": ["Application Servers"], "tags": ["tag"]},
|
||||
'murano-app-incubator/io.murano.apps.apache.Apache.zip')
|
||||
cls.tomcat_id = upload_package(
|
||||
os.path.join(cls.pkgs_path, 'io.murano.apps.apache.Apache.zip'),
|
||||
{'categories': ['Application Servers'], 'tags': ['tag']}
|
||||
)
|
||||
cls.tomcat_id = cls.client.upload_package(
|
||||
'Tomcat',
|
||||
{"categories": ["Application Servers"], "tags": ["tag"]},
|
||||
'murano-app-incubator/io.murano.apps.apache.Tomcat.zip')
|
||||
cls.telnet_id = upload_package(
|
||||
os.path.join(cls.pkgs_path, 'io.murano.apps.apache.Tomcat.zip'),
|
||||
{'categories': ['Application Servers'], 'tags': ['tag']}
|
||||
)
|
||||
cls.telnet_id = cls.client.upload_package(
|
||||
'Telnet',
|
||||
{"categories": ["Web"], "tags": ["tag"]},
|
||||
'murano-app-incubator/io.murano.apps.linux.Telnet.zip')
|
||||
cls.ad_id = upload_package(
|
||||
os.path.join(cls.pkgs_path, 'io.murano.apps.linux.Telnet.zip'),
|
||||
{'categories': ['Web'], 'tags': ['tag']}
|
||||
)
|
||||
cls.ad_id = cls.client.upload_package(
|
||||
'Active Directory',
|
||||
{"categories": ["Microsoft Services"], "tags": ["tag"]},
|
||||
'murano-app-incubator/io.murano.windows.ActiveDirectory.zip')
|
||||
os.path.join(cls.pkgs_path,
|
||||
'io.murano.windows.ActiveDirectory.zip'),
|
||||
{'categories': ['Microsoft Services'], 'tags': ['tag']}
|
||||
)
|
||||
|
||||
def setUp(self):
|
||||
super(MuranoBase, self).setUp()
|
||||
|
@ -1,8 +0,0 @@
|
||||
[murano]
|
||||
auth_url = http://172.16.116.5:5000/v2.0/
|
||||
user = kate
|
||||
password = swordfish
|
||||
tenant = kate
|
||||
murano_url = http://localhost:8082/v1/
|
||||
linux_image = default_linux
|
||||
windows_image = default_windows
|
21
murano/tests/functional/engine/config.conf.sample
Normal file
21
murano/tests/functional/engine/config.conf.sample
Normal file
@ -0,0 +1,21 @@
|
||||
[murano]
|
||||
# keystone url
|
||||
#auth_url = http://127.0.0.1:5000/v2.0/
|
||||
|
||||
# keystone user
|
||||
#user = admin
|
||||
|
||||
# password for keystone user
|
||||
#password = pass
|
||||
|
||||
# keystone tenant
|
||||
#tenant = admin
|
||||
|
||||
# murano url
|
||||
#murano_url = http://localhost:8082
|
||||
|
||||
# image for linux services
|
||||
#linux_image = default_linux
|
||||
|
||||
# image for windows services
|
||||
#windows_image = default_windows
|
@ -23,8 +23,6 @@ import murano.tests.unit.utils as test_utils
|
||||
|
||||
|
||||
class TestEnvironmentApi(tb.ControllerTest, tb.MuranoApiTestCase):
|
||||
RPC_IMPORT = 'murano.db.services.environments.rpc'
|
||||
|
||||
def setUp(self):
|
||||
super(TestEnvironmentApi, self).setUp()
|
||||
self.controller = environments.Controller()
|
||||
@ -165,8 +163,9 @@ class TestEnvironmentApi(tb.ControllerTest, tb.MuranoApiTestCase):
|
||||
self._set_policy_rules(
|
||||
{'delete_environment': '@'}
|
||||
)
|
||||
self.expect_policy_check('delete_environment',
|
||||
{'environment_id': '12345'})
|
||||
self.expect_policy_check(
|
||||
'delete_environment', {'environment_id': '12345'}
|
||||
)
|
||||
|
||||
fake_now = timeutils.utcnow()
|
||||
expected = dict(
|
||||
@ -188,6 +187,8 @@ class TestEnvironmentApi(tb.ControllerTest, tb.MuranoApiTestCase):
|
||||
test_utils.save_models(e)
|
||||
|
||||
rpc_task = {
|
||||
'id': '12345',
|
||||
'action': None,
|
||||
'tenant_id': self.tenant,
|
||||
'model': {'Attributes': {}, 'Objects': None},
|
||||
'token': None
|
||||
|
@ -17,8 +17,8 @@ import mock
|
||||
|
||||
from murano.engine.system import agent
|
||||
from murano.engine.system import agent_listener
|
||||
from murano.tests.dsl.foundation import object_model as om
|
||||
from murano.tests.dsl.foundation import test_case
|
||||
from murano.tests.unit.dsl.foundation import object_model as om
|
||||
from murano.tests.unit.dsl.foundation import test_case
|
||||
|
||||
|
||||
class TestAgentListener(test_case.DslTestCase):
|
@ -67,7 +67,7 @@ def verify_session(func):
|
||||
'is invalid').format(session_id))
|
||||
raise exc.HTTPForbidden()
|
||||
|
||||
if session.state == sessions.SessionState.deploying:
|
||||
if session.state == sessions.SessionState.DEPLOYING:
|
||||
LOG.info(_('Session <SessionId {0}> is already in '
|
||||
'deployment state').format(session_id))
|
||||
raise exc.HTTPForbidden()
|
||||
|
Loading…
x
Reference in New Issue
Block a user