New way of session handling
Change-Id: I3246fa50d2dac9a65f26045bcb9386660b602aa4
This commit is contained in:
parent
9d13fb7b24
commit
b15769420a
@ -12,7 +12,7 @@
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.from oslo.config import cfg
|
||||
# under the License.
|
||||
|
||||
import gettext
|
||||
import os
|
||||
|
69
doc/source/man/muranoapi.rst
Normal file
69
doc/source/man/muranoapi.rst
Normal file
@ -0,0 +1,69 @@
|
||||
..
|
||||
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.
|
||||
|
||||
==========
|
||||
murano-api
|
||||
==========
|
||||
|
||||
-----------------------------
|
||||
Murano API Server
|
||||
-----------------------------
|
||||
|
||||
:Author: smelikyan@mirantis.com
|
||||
:Date: 2013-04-04
|
||||
:Copyright: Mirantis, Inc.
|
||||
:Version: 2013.1-dev
|
||||
:Manual section: 1
|
||||
:Manual group: cloud computing
|
||||
|
||||
|
||||
SYNOPSIS
|
||||
========
|
||||
|
||||
murano-api [options]
|
||||
|
||||
DESCRIPTION
|
||||
===========
|
||||
|
||||
murano-api is a server daemon that serves the Murano API
|
||||
|
||||
OPTIONS
|
||||
=======
|
||||
|
||||
**General options**
|
||||
|
||||
**-v, --verbose**
|
||||
Print more verbose output
|
||||
|
||||
**--config-file**
|
||||
Config file used for running service
|
||||
|
||||
**--bind-host=HOST**
|
||||
Address of host running ``murano-api``. Defaults to `0.0.0.0`.
|
||||
|
||||
**--bind-port=PORT**
|
||||
Port that ``murano-api`` listens on. Defaults to `8082`.
|
||||
|
||||
|
||||
FILES
|
||||
=====
|
||||
|
||||
* /etc/murano-api/murano-api.conf
|
||||
* /etc/murano-api/murano-api-paste.conf
|
||||
|
||||
SEE ALSO
|
||||
========
|
||||
|
||||
* `Murano <http://murano.mirantis.com>`__
|
@ -10,4 +10,4 @@
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.from oslo.config import cfg
|
||||
# under the License.
|
||||
|
@ -10,4 +10,4 @@
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.from oslo.config import cfg
|
||||
# under the License.
|
||||
|
@ -10,7 +10,7 @@
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.from oslo.config import cfg
|
||||
# under the License.
|
||||
|
||||
from muranoapi.db.models import Session, Environment, Status
|
||||
from muranoapi.db.session import get_session
|
||||
@ -35,57 +35,6 @@ def save_draft(session_id, draft):
|
||||
session.save(unit)
|
||||
|
||||
|
||||
def get_env_status(environment_id, session_id):
|
||||
status = 'draft'
|
||||
|
||||
unit = get_session()
|
||||
|
||||
if not session_id:
|
||||
variants = ['open', 'deploying']
|
||||
session = unit.query(Session).filter(
|
||||
Session.environment_id == environment_id and
|
||||
Session.state.in_(variants)
|
||||
).first()
|
||||
if session:
|
||||
session_id = session.id
|
||||
else:
|
||||
return status
|
||||
|
||||
session_state = unit.query(Session).get(session_id).state
|
||||
reports_count = unit.query(Status).filter_by(environment_id=environment_id,
|
||||
session_id=session_id).count()
|
||||
|
||||
if session_state == 'deployed':
|
||||
status = 'finished'
|
||||
|
||||
if session_state == 'deploying' and reports_count > 1:
|
||||
status = 'pending'
|
||||
|
||||
draft = get_draft(environment_id, session_id)
|
||||
|
||||
if not 'services' in draft:
|
||||
return 'pending'
|
||||
|
||||
def get_statuses(type):
|
||||
if type in draft['services']:
|
||||
services = draft['services'][type]
|
||||
return [get_service_status(environment_id,
|
||||
session_id,
|
||||
service) for service in services]
|
||||
else:
|
||||
return []
|
||||
|
||||
is_inprogress = filter(lambda item: item == 'inprogress',
|
||||
get_statuses('activeDirectories') +
|
||||
get_statuses('webServers') +
|
||||
get_statuses('aspNetApps'))
|
||||
|
||||
if session_state == 'deploying' and is_inprogress > 1:
|
||||
status = 'inprogress'
|
||||
|
||||
return status
|
||||
|
||||
|
||||
def get_service_status(environment_id, session_id, service):
|
||||
status = 'draft'
|
||||
|
||||
|
@ -10,12 +10,11 @@
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.from oslo.config import cfg
|
||||
# under the License.
|
||||
|
||||
from muranoapi import utils
|
||||
from muranoapi.api.v1 import save_draft, get_draft, get_service_status
|
||||
from muranoapi.common import uuidutils
|
||||
from muranoapi.openstack.common import wsgi, timeutils
|
||||
from muranoapi.db.services.systemservices import SystemServices
|
||||
from muranoapi.openstack.common import wsgi
|
||||
from muranoapi.openstack.common import log as logging
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@ -23,62 +22,40 @@ log = logging.getLogger(__name__)
|
||||
|
||||
class Controller(object):
|
||||
def index(self, request, environment_id):
|
||||
log.debug(_('ActiveDirectory:Index <EnvId: {0}>'.
|
||||
format(environment_id)))
|
||||
log.debug(_('ActiveDirectory:Index '
|
||||
'<EnvId: {0}>'.format(environment_id)))
|
||||
|
||||
draft = prepare_draft(get_draft(environment_id,
|
||||
request.context.session))
|
||||
session_id = None
|
||||
if hasattr(request, 'context') and request.context.session:
|
||||
session_id = request.context.session
|
||||
|
||||
for dc in draft['services']['activeDirectories']:
|
||||
dc['status'] = get_service_status(environment_id,
|
||||
request.context.session,
|
||||
dc)
|
||||
get = SystemServices.get_services
|
||||
|
||||
return {'activeDirectories': draft['services']['activeDirectories']}
|
||||
services = get(environment_id, 'activeDirectories', session_id)
|
||||
services = [srv.to_dict() for srv in services]
|
||||
|
||||
return {'activeDirectories': services}
|
||||
|
||||
@utils.verify_session
|
||||
def create(self, request, environment_id, body):
|
||||
log.debug(_('ActiveDirectory:Create <EnvId: {0}, Body: {1}>'.
|
||||
format(environment_id, body)))
|
||||
log.debug(_('ActiveDirectory:Create <EnvId: {0}, '
|
||||
'Body: {1}>'.format(environment_id, body)))
|
||||
|
||||
draft = get_draft(session_id=request.context.session)
|
||||
session_id = request.context.session
|
||||
create = SystemServices.create_active_directory
|
||||
|
||||
active_directory = body.copy()
|
||||
active_directory['id'] = uuidutils.generate_uuid()
|
||||
active_directory['created'] = str(timeutils.utcnow())
|
||||
active_directory['updated'] = str(timeutils.utcnow())
|
||||
|
||||
unit_count = 0
|
||||
for unit in active_directory['units']:
|
||||
unit_count += 1
|
||||
unit['id'] = uuidutils.generate_uuid()
|
||||
unit['name'] = 'dc{0}'.format(unit_count)
|
||||
|
||||
draft = prepare_draft(draft)
|
||||
draft['services']['activeDirectories'].append(active_directory)
|
||||
save_draft(request.context.session, draft)
|
||||
|
||||
return active_directory
|
||||
return create(body.copy(), session_id, environment_id)
|
||||
|
||||
@utils.verify_session
|
||||
def delete(self, request, environment_id, active_directory_id):
|
||||
log.debug(_('ActiveDirectory:Delete <EnvId: {0}, Id: {1}>'.
|
||||
format(environment_id, active_directory_id)))
|
||||
log.debug(_('ActiveDirectory:Delete <EnvId: {0}, '
|
||||
'Id: {1}>'.format(environment_id, active_directory_id)))
|
||||
|
||||
draft = get_draft(session_id=request.context.session)
|
||||
items = [service for service in draft['services']['activeDirectories']
|
||||
if service['id'] != active_directory_id]
|
||||
draft['services']['activeDirectories'] = items
|
||||
save_draft(request.context.session, draft)
|
||||
session_id = request.context.session
|
||||
delete = SystemServices.delete_service
|
||||
|
||||
|
||||
def prepare_draft(draft):
|
||||
if not 'services' in draft:
|
||||
draft['services'] = {}
|
||||
|
||||
if not 'activeDirectories' in draft['services']:
|
||||
draft['services']['activeDirectories'] = []
|
||||
|
||||
return draft
|
||||
delete(active_directory_id, 'activeDirectories', session_id,
|
||||
environment_id)
|
||||
|
||||
|
||||
def create_resource():
|
||||
|
@ -10,16 +10,15 @@
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.from oslo.config import cfg
|
||||
# under the License.
|
||||
|
||||
from amqplib.client_0_8 import Message
|
||||
import anyjson
|
||||
import eventlet
|
||||
from webob import exc
|
||||
from muranoapi.common import config
|
||||
from muranoapi.api.v1 import get_env_status
|
||||
from muranoapi.db.session import get_session
|
||||
from muranoapi.db.models import Environment
|
||||
from muranoapi.db.services.environments import EnvironmentServices
|
||||
from muranoapi.db.services.systemservices import SystemServices
|
||||
from muranoapi.openstack.common import wsgi
|
||||
from muranoapi.openstack.common import log as logging
|
||||
|
||||
@ -33,35 +32,18 @@ class Controller(object):
|
||||
def index(self, request):
|
||||
log.debug(_('Environments:List'))
|
||||
|
||||
#Only environments from same tenant as users should be shown
|
||||
#Only environments from same tenant as user should be returned
|
||||
filters = {'tenant_id': request.context.tenant}
|
||||
|
||||
session = get_session()
|
||||
environments = session.query(Environment).filter_by(**filters)
|
||||
environments = EnvironmentServices.get_environments_by(filters)
|
||||
environments = [env.to_dict() for env in environments]
|
||||
|
||||
for env in environments:
|
||||
env['status'] = get_env_status(env['id'], request.context.session)
|
||||
|
||||
return {"environments": environments}
|
||||
|
||||
def create(self, request, body):
|
||||
log.debug(_('Environments:Create <Body {0}>'.format(body)))
|
||||
|
||||
#tagging environment by tenant_id for later checks
|
||||
params = body.copy()
|
||||
params['tenant_id'] = request.context.tenant
|
||||
|
||||
environment = Environment()
|
||||
environment.update(params)
|
||||
|
||||
session = get_session()
|
||||
with session.begin():
|
||||
session.add(environment)
|
||||
|
||||
#saving environment as Json to itself
|
||||
environment.update({"description": environment.to_dict()})
|
||||
environment.save(session)
|
||||
environment = EnvironmentServices.create(body.copy(),
|
||||
request.context.tenant)
|
||||
|
||||
return environment.to_dict()
|
||||
|
||||
@ -76,13 +58,30 @@ class Controller(object):
|
||||
raise exc.HTTPUnauthorized
|
||||
|
||||
env = environment.to_dict()
|
||||
env['status'] = get_env_status(environment_id, request.context.session)
|
||||
env['status'] = EnvironmentServices.get_status(env['id'])
|
||||
|
||||
session_id = None
|
||||
if hasattr(request, 'context') and request.context.session:
|
||||
session_id = request.context.session
|
||||
|
||||
#add services to env
|
||||
get = SystemServices.get_service
|
||||
|
||||
ad = get(environment_id, 'activeDirectories', session_id)
|
||||
webServers = get(environment_id, 'webServers', session_id)
|
||||
aspNetApps = get(environment_id, 'aspNetApps', session_id)
|
||||
|
||||
env['services'] = {
|
||||
'activeDirectories': ad,
|
||||
'webServers': webServers,
|
||||
'aspNetApps': aspNetApps
|
||||
}
|
||||
|
||||
return env
|
||||
|
||||
def update(self, request, environment_id, body):
|
||||
log.debug(_('Environments:Update <Id: {0}, Body: {1}>'.
|
||||
format(environment_id, body)))
|
||||
log.debug(_('Environments:Update <Id: {0}, '
|
||||
'Body: {1}>'.format(environment_id, body)))
|
||||
|
||||
session = get_session()
|
||||
environment = session.query(Environment).get(environment_id)
|
||||
@ -99,33 +98,14 @@ class Controller(object):
|
||||
def delete(self, request, environment_id):
|
||||
log.debug(_('Environments:Delete <Id: {0}>'.format(environment_id)))
|
||||
|
||||
session = get_session()
|
||||
environment = session.query(Environment).get(environment_id)
|
||||
unit = get_session()
|
||||
environment = unit.query(Environment).get(environment_id)
|
||||
|
||||
with session.begin():
|
||||
session.delete(environment)
|
||||
if environment.tenant_id != request.context.tenant:
|
||||
log.info('User is not authorized to access this tenant resources.')
|
||||
raise exc.HTTPUnauthorized
|
||||
|
||||
#preparing data for removal from conductor
|
||||
env = environment.description
|
||||
env['services'] = []
|
||||
env['deleted'] = True
|
||||
#Set X-Auth-Token for conductor
|
||||
env['token'] = request.context.auth_token
|
||||
|
||||
connection = amqp.Connection('{0}:{1}'.
|
||||
format(rabbitmq.host, rabbitmq.port),
|
||||
virtual_host=rabbitmq.virtual_host,
|
||||
userid=rabbitmq.login,
|
||||
password=rabbitmq.password,
|
||||
ssl=rabbitmq.use_ssl, insist=True)
|
||||
channel = connection.channel()
|
||||
channel.exchange_declare('tasks', 'direct', durable=True,
|
||||
auto_delete=False)
|
||||
|
||||
channel.basic_publish(Message(body=anyjson.serialize(env)), 'tasks',
|
||||
'tasks')
|
||||
|
||||
return None
|
||||
EnvironmentServices.delete(environment_id, request.context.auth_token)
|
||||
|
||||
|
||||
def create_resource():
|
||||
|
@ -10,7 +10,7 @@
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.from oslo.config import cfg
|
||||
# under the License.
|
||||
|
||||
import routes
|
||||
from muranoapi.openstack.common import wsgi
|
||||
@ -50,10 +50,6 @@ class API(wsgi.Router):
|
||||
conditions={'method': ['DELETE']})
|
||||
|
||||
sessions_resource = sessions.create_resource()
|
||||
mapper.connect('/environments/{environment_id}/sessions',
|
||||
controller=sessions_resource,
|
||||
action='index',
|
||||
conditions={'method': ['GET']})
|
||||
mapper.connect('/environments/{environment_id}/configure',
|
||||
controller=sessions_resource,
|
||||
action='configure',
|
||||
|
@ -10,93 +10,95 @@
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.from oslo.config import cfg
|
||||
# under the License.
|
||||
|
||||
from amqplib.client_0_8 import Message
|
||||
import anyjson
|
||||
import eventlet
|
||||
from webob import exc
|
||||
from muranoapi.common import config
|
||||
from muranoapi.db.models import Session, Status, Environment
|
||||
from muranoapi.db.models import Session, Status
|
||||
from muranoapi.db.session import get_session
|
||||
from muranoapi.db.services.sessions import SessionServices
|
||||
from muranoapi.db.services.sessions import SessionState
|
||||
from muranoapi.db.services.environments import EnvironmentServices
|
||||
from muranoapi.db.services.environments import EnvironmentStatus
|
||||
from muranoapi.openstack.common import wsgi
|
||||
from muranoapi.openstack.common import log as logging
|
||||
|
||||
amqp = eventlet.patcher.import_patched('amqplib.client_0_8')
|
||||
rabbitmq = config.CONF.rabbitmq
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Controller(object):
|
||||
def index(self, request, environment_id):
|
||||
log.debug(_('Session:List <EnvId: {0}>'.format(environment_id)))
|
||||
|
||||
filters = {'environment_id': environment_id,
|
||||
'user_id': request.context.user}
|
||||
|
||||
unit = get_session()
|
||||
configuration_sessions = unit.query(Session).filter_by(**filters)
|
||||
|
||||
sessions = [session.to_dict() for session in configuration_sessions if
|
||||
session.environment.tenant_id == request.context.tenant]
|
||||
return {"sessions": sessions}
|
||||
|
||||
def configure(self, request, environment_id):
|
||||
log.debug(_('Session:Configure <EnvId: {0}>'.format(environment_id)))
|
||||
|
||||
params = {'environment_id': environment_id,
|
||||
'user_id': request.context.user, 'state': 'open'}
|
||||
# no new session can be opened if environment has deploying status
|
||||
env_status = EnvironmentServices.get_status(environment_id)
|
||||
if env_status == EnvironmentStatus.deploying:
|
||||
log.info('Could not open session for environment <EnvId: {0}>,'
|
||||
'environment has deploying '
|
||||
'status.'.format(environment_id))
|
||||
raise exc.HTTPForbidden()
|
||||
|
||||
session = Session()
|
||||
session.update(params)
|
||||
|
||||
unit = get_session()
|
||||
if unit.query(Session).filter(
|
||||
Session.environment_id == environment_id and
|
||||
Session.state.in_(['open', 'deploying'])
|
||||
).first():
|
||||
log.info('There is already open session for this environment')
|
||||
raise exc.HTTPConflict
|
||||
|
||||
#create draft for apply later changes
|
||||
environment = unit.query(Environment).get(environment_id)
|
||||
session.description = environment.description
|
||||
|
||||
with unit.begin():
|
||||
unit.add(session)
|
||||
user_id = request.context.user
|
||||
session = SessionServices.create(environment_id, user_id)
|
||||
|
||||
return session.to_dict()
|
||||
|
||||
def show(self, request, environment_id, session_id):
|
||||
log.debug(_('Session:Show <EnvId: {0}, '
|
||||
'SessionId: {1}>'.format(environment_id, session_id)))
|
||||
def show(self, request, session_id):
|
||||
log.debug(_('Session:Show <SessionId: {0}>'.format(session_id)))
|
||||
|
||||
unit = get_session()
|
||||
session = unit.query(Session).get(session_id)
|
||||
|
||||
if session.environment.tenant_id != request.context.tenant:
|
||||
log.info('User is not authorized to access this tenant resources.')
|
||||
raise exc.HTTPUnauthorized
|
||||
user_id = request.context.user
|
||||
if session.user_id != user_id:
|
||||
log.info('User <UserId {0}> is not authorized to access '
|
||||
'session <SessionId {1}>.'.format(user_id, session_id))
|
||||
raise exc.HTTPUnauthorized()
|
||||
|
||||
if not SessionServices.validate(session):
|
||||
log.info('Session <SessionId {0}> is invalid'.format(session_id))
|
||||
raise exc.HTTPForbidden()
|
||||
|
||||
return session.to_dict()
|
||||
|
||||
def delete(self, request, environment_id, session_id):
|
||||
log.debug(_('Session:Delete <EnvId: {0}, '
|
||||
'SessionId: {1}>'.format(environment_id, session_id)))
|
||||
def delete(self, request, session_id):
|
||||
log.debug(_('Session:Delete <SessionId: {0}>'.format(session_id)))
|
||||
|
||||
unit = get_session()
|
||||
session = unit.query(Session).get(session_id)
|
||||
|
||||
comment = 'Session object in \'deploying\' state could not be deleted'
|
||||
if session.state == 'deploying':
|
||||
log.info(comment)
|
||||
raise exc.HTTPForbidden(comment=comment)
|
||||
user_id = request.context.user
|
||||
if session.user_id != user_id:
|
||||
log.info('User <UserId {0}> is not authorized to access '
|
||||
'session <SessionId {1}>.'.format(user_id, session_id))
|
||||
raise exc.HTTPUnauthorized()
|
||||
|
||||
if session.state == SessionState.deploying:
|
||||
log.info('Session <SessionId: {0}> is in deploying state and '
|
||||
'could not be deleted'.format(session_id))
|
||||
raise exc.HTTPForbidden()
|
||||
|
||||
with unit.begin():
|
||||
unit.delete(session)
|
||||
|
||||
return None
|
||||
|
||||
def deploy(self, request, session_id):
|
||||
log.debug(_('Session:Deploy <SessionId: {0}>'.format(session_id)))
|
||||
|
||||
unit = get_session()
|
||||
session = unit.query(Session).get(session_id)
|
||||
|
||||
if not SessionServices.validate(session):
|
||||
log.info('Session <SessionId {0}> is invalid'.format(session_id))
|
||||
raise exc.HTTPForbidden()
|
||||
|
||||
if session.state != SessionState.open:
|
||||
log.info('Session <SessionId {0}> is already deployed or '
|
||||
'deployment is in progress'.format(session_id))
|
||||
raise exc.HTTPForbidden()
|
||||
|
||||
SessionServices.deploy(session, request.context.auth_token)
|
||||
|
||||
def reports(self, request, environment_id, session_id):
|
||||
log.debug(_('Session:Reports <EnvId: {0}, '
|
||||
'SessionId: {1}>'.format(environment_id, session_id)))
|
||||
@ -110,11 +112,11 @@ class Controller(object):
|
||||
|
||||
environment = unit.query(Session).get(session_id).description
|
||||
services = []
|
||||
if 'services' in environment and 'activeDirectories' in\
|
||||
if 'services' in environment and 'activeDirectories' in \
|
||||
environment['services']:
|
||||
services += environment['services']['activeDirectories']
|
||||
|
||||
if 'services' in environment and 'webServers' in\
|
||||
if 'services' in environment and 'webServers' in \
|
||||
environment['services']:
|
||||
services += environment['services']['webServers']
|
||||
|
||||
@ -135,38 +137,6 @@ class Controller(object):
|
||||
|
||||
return {'reports': [status.to_dict() for status in result]}
|
||||
|
||||
def deploy(self, request, environment_id, session_id):
|
||||
log.debug(_('Session:Deploy <EnvId: {0}, '
|
||||
'SessionId: {1}>'.format(environment_id, session_id)))
|
||||
|
||||
unit = get_session()
|
||||
session = unit.query(Session).get(session_id)
|
||||
|
||||
msg = _('Could not deploy session. Session is already '
|
||||
'deployed or in deployment state')
|
||||
if session.state != 'open':
|
||||
log.warn(msg)
|
||||
|
||||
session.state = 'deploying'
|
||||
session.save(unit)
|
||||
|
||||
#Set X-Auth-Token for conductor
|
||||
env = session.description
|
||||
env['token'] = request.context.auth_token
|
||||
|
||||
connection = amqp.Connection('{0}:{1}'.
|
||||
format(rabbitmq.host, rabbitmq.port),
|
||||
virtual_host=rabbitmq.virtual_host,
|
||||
userid=rabbitmq.login,
|
||||
password=rabbitmq.password,
|
||||
ssl=rabbitmq.use_ssl, insist=True)
|
||||
channel = connection.channel()
|
||||
channel.exchange_declare('tasks', 'direct', durable=True,
|
||||
auto_delete=False)
|
||||
|
||||
channel.basic_publish(Message(body=anyjson.serialize(env)), 'tasks',
|
||||
'tasks')
|
||||
|
||||
|
||||
def create_resource():
|
||||
return wsgi.Resource(Controller())
|
||||
|
@ -10,12 +10,11 @@
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.from oslo.config import cfg
|
||||
# under the License.
|
||||
|
||||
from muranoapi import utils
|
||||
from muranoapi.api.v1 import save_draft, get_draft, get_service_status
|
||||
from muranoapi.common import uuidutils
|
||||
from muranoapi.openstack.common import wsgi, timeutils
|
||||
from muranoapi.db.services.systemservices import SystemServices
|
||||
from muranoapi.openstack.common import wsgi
|
||||
from muranoapi.openstack.common import log as logging
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@ -25,60 +24,36 @@ class Controller(object):
|
||||
def index(self, request, environment_id):
|
||||
log.debug(_('WebServer:List <EnvId: {0}>'.format(environment_id)))
|
||||
|
||||
draft = prepare_draft(get_draft(environment_id,
|
||||
request.context.session))
|
||||
session_id = None
|
||||
if hasattr(request, 'context') and request.context.session:
|
||||
session_id = request.context.session
|
||||
|
||||
for dc in draft['services']['webServers']:
|
||||
dc['status'] = get_service_status(environment_id,
|
||||
request.context.session, dc)
|
||||
get = SystemServices.get_services
|
||||
|
||||
return {'webServers': draft['services']['webServers']}
|
||||
services = get(environment_id, 'webServers', session_id)
|
||||
services = [srv.to_dict() for srv in services]
|
||||
|
||||
return {'webServers': services}
|
||||
|
||||
@utils.verify_session
|
||||
def create(self, request, environment_id, body):
|
||||
log.debug(_('WebServer:Create <EnvId: {0}, Body: {1}>'.
|
||||
format(environment_id, body)))
|
||||
log.debug(_('WebServer:Create <EnvId: {0}, '
|
||||
'Body: {1}>'.format(environment_id, body)))
|
||||
|
||||
draft = get_draft(session_id=request.context.session)
|
||||
session_id = request.context.session
|
||||
create = SystemServices.create_web_server
|
||||
|
||||
webServer = body.copy()
|
||||
webServer['id'] = uuidutils.generate_uuid()
|
||||
webServer['created'] = str(timeutils.utcnow())
|
||||
webServer['updated'] = str(timeutils.utcnow())
|
||||
|
||||
unit_count = 0
|
||||
for unit in webServer['units']:
|
||||
unit_count += 1
|
||||
unit['id'] = uuidutils.generate_uuid()
|
||||
unit['name'] = webServer['name'] + '_instance_' + str(unit_count)
|
||||
|
||||
draft = prepare_draft(draft)
|
||||
draft['services']['webServers'].append(webServer)
|
||||
save_draft(request.context.session, draft)
|
||||
|
||||
return webServer
|
||||
return create(body.copy(), session_id, environment_id)
|
||||
|
||||
@utils.verify_session
|
||||
def delete(self, request, environment_id, web_server_id):
|
||||
log.debug(_('WebServer:Delete <EnvId: {0}, Id: {1}>'.
|
||||
format(environment_id, web_server_id)))
|
||||
log.debug(_('WebServer:Delete <EnvId: {0}, '
|
||||
'Id: {1}>'.format(environment_id, web_server_id)))
|
||||
|
||||
draft = get_draft(session_id=request.context.session)
|
||||
session_id = request.context.session
|
||||
delete = SystemServices.delete_service
|
||||
|
||||
elements = [service for service in draft['services']['webServers'] if
|
||||
service['id'] != web_server_id]
|
||||
draft['services']['webServers'] = elements
|
||||
save_draft(request.context.session, draft)
|
||||
|
||||
|
||||
def prepare_draft(draft):
|
||||
if not 'services' in draft:
|
||||
draft['services'] = {}
|
||||
|
||||
if not 'webServers' in draft['services']:
|
||||
draft['services']['webServers'] = []
|
||||
|
||||
return draft
|
||||
delete(web_server_id, 'webServers', session_id, environment_id)
|
||||
|
||||
|
||||
def create_resource():
|
||||
|
@ -10,4 +10,4 @@
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.from oslo.config import cfg
|
||||
# under the License.
|
||||
|
@ -29,6 +29,7 @@ import sys
|
||||
from oslo.config import cfg
|
||||
from paste import deploy
|
||||
|
||||
from muranoapi.openstack.common import log
|
||||
from muranoapi.version import version_info as version
|
||||
|
||||
paste_deploy_opts = [
|
||||
@ -76,6 +77,14 @@ CONF.import_opt('use_syslog', 'muranoapi.openstack.common.log')
|
||||
CONF.import_opt('syslog_log_facility', 'muranoapi.openstack.common.log')
|
||||
|
||||
|
||||
cfg.set_defaults(log.log_opts,
|
||||
default_log_levels=['amqplib=WARN',
|
||||
'qpid.messaging=INFO',
|
||||
'sqlalchemy=WARN',
|
||||
'keystoneclient=INFO',
|
||||
'eventlet.wsgi.server=WARN'])
|
||||
|
||||
|
||||
def parse_args(args=None, usage=None, default_config_files=None):
|
||||
CONF(args=args,
|
||||
project='muranoapi',
|
||||
|
@ -10,7 +10,7 @@
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.from oslo.config import cfg
|
||||
# under the License.
|
||||
|
||||
import anyjson
|
||||
from eventlet import patcher
|
||||
|
@ -10,7 +10,7 @@
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.from oslo.config import cfg
|
||||
# under the License.
|
||||
|
||||
import uuid
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.from oslo.config import cfg
|
||||
# under the License.
|
||||
|
||||
from oslo.config import cfg
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.from oslo.config import cfg
|
||||
# under the License.
|
||||
|
||||
|
||||
from migrate.versioning.shell import main
|
||||
|
@ -10,7 +10,7 @@
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.from oslo.config import cfg
|
||||
# under the License.
|
||||
|
||||
from sqlalchemy.schema import MetaData, Table, Column, ForeignKey
|
||||
from sqlalchemy.types import String, Text, DateTime
|
||||
|
@ -10,7 +10,7 @@
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.from oslo.config import cfg
|
||||
# under the License.
|
||||
|
||||
from sqlalchemy.schema import MetaData, Table, Column, ForeignKey
|
||||
from sqlalchemy.types import String, Text, DateTime
|
||||
|
@ -10,7 +10,7 @@
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.from oslo.config import cfg
|
||||
# under the License.
|
||||
|
||||
from sqlalchemy.schema import MetaData, Table, Column, ForeignKey
|
||||
from sqlalchemy.types import String, Text, DateTime
|
||||
|
@ -10,7 +10,7 @@
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.from oslo.config import cfg
|
||||
# under the License.
|
||||
|
||||
from sqlalchemy.schema import MetaData, Table, Column
|
||||
from sqlalchemy.types import Text
|
||||
|
@ -10,7 +10,7 @@
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.from oslo.config import cfg
|
||||
# under the License.
|
||||
|
||||
from sqlalchemy.schema import MetaData, Table, Column, ForeignKey
|
||||
from sqlalchemy.types import String, Text, DateTime
|
||||
|
@ -10,7 +10,7 @@
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.from oslo.config import cfg
|
||||
# under the License.
|
||||
|
||||
from sqlalchemy.schema import MetaData, Table, Column
|
||||
from sqlalchemy.types import String
|
||||
|
@ -0,0 +1,31 @@
|
||||
# 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.
|
||||
|
||||
from sqlalchemy.schema import MetaData, Table, Column
|
||||
from sqlalchemy.types import BigInteger
|
||||
|
||||
meta = MetaData()
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
meta.bind = migrate_engine
|
||||
environment = Table('environment', meta, autoload=True)
|
||||
version = Column('version', BigInteger, nullable=False, server_default='0')
|
||||
version.create(environment)
|
||||
|
||||
|
||||
def downgrade(migrate_engine):
|
||||
meta.bind = migrate_engine
|
||||
environment = Table('environment', meta, autoload=True)
|
||||
environment.c.version.drop()
|
@ -0,0 +1,31 @@
|
||||
# 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.
|
||||
|
||||
from sqlalchemy.schema import MetaData, Table, Column
|
||||
from sqlalchemy.types import BigInteger
|
||||
|
||||
meta = MetaData()
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
meta.bind = migrate_engine
|
||||
session = Table('session', meta, autoload=True)
|
||||
version = Column('version', BigInteger, nullable=False, server_default='0')
|
||||
version.create(session)
|
||||
|
||||
|
||||
def downgrade(migrate_engine):
|
||||
meta.bind = migrate_engine
|
||||
session = Table('session', meta, autoload=True)
|
||||
session.c.version.drop()
|
@ -51,10 +51,12 @@ class ModelBase(object):
|
||||
|
||||
def update(self, values):
|
||||
"""dict.update() behaviour."""
|
||||
self.updated = timeutils.utcnow()
|
||||
for k, v in values.iteritems():
|
||||
self[k] = v
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self.updated = timeutils.utcnow()
|
||||
setattr(self, key, value)
|
||||
|
||||
def __getitem__(self, key):
|
||||
@ -79,8 +81,8 @@ class ModelBase(object):
|
||||
|
||||
def to_dict(self):
|
||||
dictionary = self.__dict__.copy()
|
||||
return dict([(k, v) for k, v in dictionary.iteritems()
|
||||
if k != '_sa_instance_state'])
|
||||
return dict((k, v) for k, v in dictionary.iteritems()
|
||||
if k != '_sa_instance_state')
|
||||
|
||||
|
||||
class JsonBlob(TypeDecorator):
|
||||
@ -100,6 +102,7 @@ class Environment(BASE, ModelBase):
|
||||
id = Column(String(32), primary_key=True, default=uuidutils.generate_uuid)
|
||||
name = Column(String(255), nullable=False)
|
||||
tenant_id = Column(String(32), nullable=False)
|
||||
version = Column(BigInteger, nullable=False, default=0)
|
||||
description = Column(JsonBlob(), nullable=False, default={})
|
||||
|
||||
sessions = relationship("Session", backref='environment',
|
||||
@ -122,6 +125,7 @@ class Session(BASE, ModelBase):
|
||||
user_id = Column(String(36), nullable=False)
|
||||
state = Column(String(36), nullable=False)
|
||||
description = Column(JsonBlob(), nullable=False)
|
||||
version = Column(BigInteger, nullable=False, default=0)
|
||||
|
||||
def to_dict(self):
|
||||
dictionary = super(Session, self).to_dict()
|
||||
|
13
muranoapi/db/services/__init__.py
Normal file
13
muranoapi/db/services/__init__.py
Normal file
@ -0,0 +1,13 @@
|
||||
# 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.
|
180
muranoapi/db/services/environments.py
Normal file
180
muranoapi/db/services/environments.py
Normal file
@ -0,0 +1,180 @@
|
||||
# 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.
|
||||
from collections import namedtuple
|
||||
|
||||
from amqplib.client_0_8 import Message
|
||||
import anyjson
|
||||
import eventlet
|
||||
from muranoapi.common import config
|
||||
from muranoapi.db.models import Session, Environment
|
||||
from muranoapi.db.session import get_session
|
||||
from sessions import SessionServices, SessionState
|
||||
|
||||
|
||||
amqp = eventlet.patcher.import_patched('amqplib.client_0_8')
|
||||
rabbitmq = config.CONF.rabbitmq
|
||||
|
||||
EnvironmentStatus = namedtuple('EnvironmentStatus', [
|
||||
'ready', 'pending', 'deploying'
|
||||
])(
|
||||
ready='ready', pending='pending', deploying='deploying'
|
||||
)
|
||||
|
||||
|
||||
class EnvironmentServices(object):
|
||||
@staticmethod
|
||||
def get_environments_by(filters):
|
||||
"""
|
||||
Returns list of environments
|
||||
:param filters: property filters
|
||||
:return: Returns list of environments
|
||||
"""
|
||||
unit = get_session()
|
||||
environments = unit.query(Environment).filter_by(**filters)
|
||||
|
||||
for env in environments:
|
||||
env['status'] = EnvironmentServices.get_status(env['id'])
|
||||
|
||||
return environments
|
||||
|
||||
@staticmethod
|
||||
def get_status(environment_id):
|
||||
"""
|
||||
Environment can have one of three distinguished 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`.
|
||||
|
||||
:param environment_id: Id of environment for which we checking status.
|
||||
:return: Environment status
|
||||
"""
|
||||
#Ready: there are no sessions in status `deploying` or `open`
|
||||
status = 'ready'
|
||||
|
||||
#Deploying: there is at least one valid session with status `deploying`
|
||||
deploying = SessionServices.get_sessions(environment_id,
|
||||
SessionState.deploying)
|
||||
if len(deploying) > 0:
|
||||
status = 'deploying'
|
||||
|
||||
#Pending: there is at least one valid session with status `open`;
|
||||
open = SessionServices.get_sessions(environment_id, SessionState.open)
|
||||
if len(open) > 0:
|
||||
status = 'pending'
|
||||
|
||||
return status
|
||||
|
||||
@staticmethod
|
||||
def create(environment_params, tenant_id):
|
||||
#tagging environment by tenant_id for later checks
|
||||
"""
|
||||
Creates environment with specified params, in particular - name
|
||||
:param environment_params: Dict, e.g. {'name': 'env-name'}
|
||||
:param tenant_id: Tenant Id
|
||||
:return: Created Environment
|
||||
"""
|
||||
environment_params['tenant_id'] = tenant_id
|
||||
|
||||
environment = Environment()
|
||||
environment.update(environment_params)
|
||||
|
||||
unit = get_session()
|
||||
with unit.begin():
|
||||
unit.add(environment)
|
||||
|
||||
#saving environment as Json to itself
|
||||
environment.update({"description": environment.to_dict()})
|
||||
environment.save(unit)
|
||||
|
||||
return environment
|
||||
|
||||
@staticmethod
|
||||
def delete(environment_id, token):
|
||||
"""
|
||||
Deletes environment and notify orchestration engine about deletion
|
||||
|
||||
:param environment_id: Environment that is going to be deleted
|
||||
:param token: OpenStack auth token
|
||||
"""
|
||||
unit = get_session()
|
||||
environment = unit.query(Environment).get(environment_id)
|
||||
|
||||
with unit.begin():
|
||||
unit.delete(environment)
|
||||
|
||||
#preparing data for removal from conductor
|
||||
env = environment.description
|
||||
env['services'] = {}
|
||||
env['deleted'] = True
|
||||
|
||||
#Set X-Auth-Token for conductor
|
||||
env['token'] = token
|
||||
|
||||
connection = amqp.Connection('{0}:{1}'.
|
||||
format(rabbitmq.host, rabbitmq.port),
|
||||
virtual_host=rabbitmq.virtual_host,
|
||||
userid=rabbitmq.login,
|
||||
password=rabbitmq.password,
|
||||
ssl=rabbitmq.use_ssl, insist=True)
|
||||
channel = connection.channel()
|
||||
channel.exchange_declare('tasks', 'direct', durable=True,
|
||||
auto_delete=False)
|
||||
|
||||
channel.basic_publish(Message(body=anyjson.serialize(env)), 'tasks',
|
||||
'tasks')
|
||||
|
||||
@staticmethod
|
||||
def get_environment_description(environment_id, session_id=None):
|
||||
"""
|
||||
Returns environment description for specified environment. If session
|
||||
is specified and not in deploying state function returns modified
|
||||
environment description, otherwise returns actual environment desc.
|
||||
|
||||
:param environment_id: Environment Id
|
||||
:param session_id: Session Id
|
||||
:return: Environment Description Object
|
||||
"""
|
||||
unit = get_session()
|
||||
|
||||
if session_id:
|
||||
session = unit.query(Session).get(session_id)
|
||||
if SessionServices.validate(session):
|
||||
if session.state != SessionState.deployed:
|
||||
env_description = session.description
|
||||
else:
|
||||
env = unit.query(Environment).get(session.environment_id)
|
||||
env_description = env.description
|
||||
else:
|
||||
env = unit.query(Environment).get(session.environment_id)
|
||||
env_description = env.description
|
||||
else:
|
||||
env = (unit.query(Environment).get(environment_id))
|
||||
env_description = env.description
|
||||
|
||||
return env_description
|
||||
|
||||
@staticmethod
|
||||
def save_environment_description(session_id, environment):
|
||||
"""
|
||||
Saves environment description to specified session
|
||||
|
||||
:param session_id: Session Id
|
||||
:param environment: Environment Description
|
||||
"""
|
||||
unit = get_session()
|
||||
session = unit.query(Session).get(session_id)
|
||||
|
||||
session.description = environment
|
||||
session.save(unit)
|
140
muranoapi/db/services/sessions.py
Normal file
140
muranoapi/db/services/sessions.py
Normal file
@ -0,0 +1,140 @@
|
||||
# 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.
|
||||
from collections import namedtuple
|
||||
|
||||
from amqplib.client_0_8 import Message
|
||||
import anyjson
|
||||
import eventlet
|
||||
from muranoapi.common import config
|
||||
from muranoapi.db.models import Session, Environment
|
||||
from muranoapi.db.session import get_session
|
||||
|
||||
|
||||
amqp = eventlet.patcher.import_patched('amqplib.client_0_8')
|
||||
rabbitmq = config.CONF.rabbitmq
|
||||
|
||||
SessionState = namedtuple('SessionState', ['open', 'deploying', 'deployed'])(
|
||||
open='open', deploying='deploying', deployed='deployed'
|
||||
)
|
||||
|
||||
|
||||
class SessionServices(object):
|
||||
@staticmethod
|
||||
def get_sessions(environment_id, state=None):
|
||||
"""
|
||||
Get list of sessions for specified environment
|
||||
|
||||
:param environment_id: Environment Id
|
||||
:param state: glazierapi.db.services.environments.EnvironmentStatus
|
||||
:return: Sessions for specified Environment, if SessionState is
|
||||
not defined all sessions for specified environment is returned.
|
||||
"""
|
||||
|
||||
unit = get_session()
|
||||
# Here we duplicate logic for reducing calls to database
|
||||
# Checks for validation is same as in validate.
|
||||
environment = unit.query(Environment).get(environment_id)
|
||||
|
||||
return unit.query(Session).filter(
|
||||
#Get all session for this environment
|
||||
Session.environment_id == environment_id,
|
||||
#in this state, if state is not specified return in all states
|
||||
Session.state.in_(SessionState if state is None else [state]),
|
||||
#Only sessions with same version as current env version are valid
|
||||
Session.version == environment.version
|
||||
).all()
|
||||
|
||||
@staticmethod
|
||||
def create(environment_id, user_id):
|
||||
"""
|
||||
Creates session object for specific environment for specified user.
|
||||
|
||||
:param environment_id: Environment Id
|
||||
:param user_id: User Id
|
||||
:return: Created session
|
||||
"""
|
||||
unit = get_session()
|
||||
environment = unit.query(Environment).get(environment_id)
|
||||
|
||||
session = Session()
|
||||
session.environment_id = environment.id
|
||||
session.user_id = user_id
|
||||
session.state = SessionState.open
|
||||
# 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
|
||||
# environment only after deployment completed
|
||||
session.description = environment.description
|
||||
|
||||
with unit.begin():
|
||||
unit.add(session)
|
||||
|
||||
return session
|
||||
|
||||
@staticmethod
|
||||
def validate(session):
|
||||
"""
|
||||
Session is valid only if no other session for same
|
||||
environment was already deployed on in deploying state,
|
||||
|
||||
:param session: Session for validation
|
||||
"""
|
||||
|
||||
#if other session is deploying now current session is invalid
|
||||
unit = get_session()
|
||||
other_is_deploying = unit.query(Session).filter_by(
|
||||
environment_id=session.environment_id, state=SessionState.deploying
|
||||
).count() > 0
|
||||
if session.state == SessionState.open and other_is_deploying:
|
||||
return False
|
||||
|
||||
#if environment version is higher then version on which current session
|
||||
#is created then other session was already deployed
|
||||
current_env = unit.query(Environment).get(session.environment_id)
|
||||
if current_env.version > session.version:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def deploy(session, token):
|
||||
"""
|
||||
Prepares environment for deployment and send deployment command to
|
||||
orchestration engine
|
||||
|
||||
:param session: session that is going to be deployed
|
||||
:param token: auth token that is going to be used by orchestration
|
||||
"""
|
||||
unit = get_session()
|
||||
|
||||
#Set X-Auth-Token for conductor
|
||||
environment = session.description
|
||||
environment['token'] = token
|
||||
|
||||
session.state = SessionState.deploying
|
||||
session.save(unit)
|
||||
|
||||
connection = amqp.Connection('{0}:{1}'.
|
||||
format(rabbitmq.host, rabbitmq.port),
|
||||
virtual_host=rabbitmq.virtual_host,
|
||||
userid=rabbitmq.login,
|
||||
password=rabbitmq.password,
|
||||
ssl=rabbitmq.use_ssl, insist=True)
|
||||
channel = connection.channel()
|
||||
channel.exchange_declare('tasks', 'direct', durable=True,
|
||||
auto_delete=False)
|
||||
|
||||
channel.basic_publish(
|
||||
Message(body=anyjson.serialize(environment)), 'tasks', 'tasks'
|
||||
)
|
191
muranoapi/db/services/systemservices.py
Normal file
191
muranoapi/db/services/systemservices.py
Normal file
@ -0,0 +1,191 @@
|
||||
# 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 eventlet
|
||||
from muranoapi.common import config
|
||||
from muranoapi.db.services.environments import EnvironmentServices
|
||||
from muranoapi.openstack.common import uuidutils, timeutils
|
||||
|
||||
|
||||
amqp = eventlet.patcher.import_patched('amqplib.client_0_8')
|
||||
rabbitmq = config.CONF.rabbitmq
|
||||
|
||||
|
||||
class SystemServices(object):
|
||||
@staticmethod
|
||||
def get_service_status(environment_id, service_id):
|
||||
"""
|
||||
Service can have one of three distinguished statuses:
|
||||
|
||||
- Deploying: if environment has status deploying and there is at least
|
||||
one orchestration engine report for this service;
|
||||
- Pending: if environment has status `deploying` and there is no
|
||||
report from orchestration engine about this service;
|
||||
- Ready: If environment has status ready.
|
||||
|
||||
:param environment_id: Service environment, we always know to which
|
||||
environment service belongs to
|
||||
:param service_id: Id of service for which we checking status.
|
||||
:return: Service status
|
||||
"""
|
||||
# Now we assume that service has same status as environment.
|
||||
# TODO: implement as designed and described above
|
||||
|
||||
return EnvironmentServices.get_status(environment_id)
|
||||
|
||||
@staticmethod
|
||||
def get_services(environment_id, service_type, session_id=None):
|
||||
"""
|
||||
Get services of specified service_type from specified environment.
|
||||
If session_id is specified session state is checked, and if session is
|
||||
not deployed function returns service from the modified environment.
|
||||
|
||||
:param environment_id: Environment Id
|
||||
:param service_type: Service service_type, e.g. activeDirectories
|
||||
:param session_id: Session Id
|
||||
:return: Service Object List
|
||||
"""
|
||||
env_description = EnvironmentServices.get_environment_description(
|
||||
environment_id, session_id)
|
||||
|
||||
if not 'services' in env_description:
|
||||
return []
|
||||
|
||||
if service_type in env_description['services']:
|
||||
services = env_description['services'][service_type]
|
||||
for service in services:
|
||||
service['status'] = SystemServices.get_service_status(
|
||||
environment_id, None)
|
||||
return services
|
||||
else:
|
||||
return []
|
||||
|
||||
@staticmethod
|
||||
def get_service(environment_id, service_id, session_id=None):
|
||||
"""
|
||||
Get services from specified environment. If session_id is specified
|
||||
session state is checked, and if session is not deployed function
|
||||
returns service from the modified environment.
|
||||
|
||||
:param environment_id: Environment Id
|
||||
:param service_id: Service Id
|
||||
:param session_id: Session Id
|
||||
:return: Service Object
|
||||
:raise: ValueError if no services described in environment or if
|
||||
service not found
|
||||
"""
|
||||
env_description = EnvironmentServices.get_environment_description(
|
||||
environment_id, session_id)
|
||||
|
||||
if not 'services' in env_description:
|
||||
raise ValueError('This environment does not have services')
|
||||
|
||||
services = []
|
||||
if 'activeDirectories' in env_description['services']:
|
||||
services = env_description['services']['activeDirectories']
|
||||
|
||||
if 'webServers' in env_description['services']:
|
||||
services += env_description['services']['webServers']
|
||||
|
||||
services = filter(lambda s: s.id == service_id, services)
|
||||
|
||||
if len(services) > 0:
|
||||
return services[0]
|
||||
|
||||
raise ValueError('Service with specified id does not exist')
|
||||
|
||||
@staticmethod
|
||||
def create_active_directory(ad_params, session_id, environment_id):
|
||||
"""
|
||||
Creates active directory service and saves it in specified session
|
||||
:param ad_params: Active Directory Params as Dict
|
||||
:param session_id: Session
|
||||
"""
|
||||
env_description = EnvironmentServices.get_environment_description(
|
||||
environment_id, session_id)
|
||||
|
||||
active_directory = ad_params
|
||||
active_directory['id'] = uuidutils.generate_uuid()
|
||||
active_directory['created'] = str(timeutils.utcnow())
|
||||
active_directory['updated'] = str(timeutils.utcnow())
|
||||
|
||||
unit_count = 0
|
||||
for unit in active_directory['units']:
|
||||
unit_count += 1
|
||||
unit['id'] = uuidutils.generate_uuid()
|
||||
unit['name'] = 'dc{0}'.format(unit_count)
|
||||
|
||||
if not 'services' in env_description:
|
||||
env_description['services'] = {}
|
||||
|
||||
if not 'activeDirectories' in env_description['services']:
|
||||
env_description['services']['activeDirectories'] = []
|
||||
|
||||
env_description['services']['activeDirectories'].append(
|
||||
active_directory)
|
||||
EnvironmentServices.save_environment_description(session_id,
|
||||
env_description)
|
||||
|
||||
return active_directory
|
||||
|
||||
@staticmethod
|
||||
def create_web_server(ws_params, session_id, environment_id):
|
||||
"""
|
||||
Creates web server service and saves it in specified session
|
||||
:param ws_params: Web Server Params as Dict
|
||||
:param session_id: Session
|
||||
"""
|
||||
env_description = EnvironmentServices.get_environment_description(
|
||||
environment_id, session_id)
|
||||
|
||||
web_server = ws_params
|
||||
web_server['id'] = uuidutils.generate_uuid()
|
||||
web_server['created'] = str(timeutils.utcnow())
|
||||
web_server['updated'] = str(timeutils.utcnow())
|
||||
|
||||
unit_count = 0
|
||||
for unit in web_server['units']:
|
||||
unit_count += 1
|
||||
unit['id'] = uuidutils.generate_uuid()
|
||||
unit['name'] = web_server['name'] + '_instance_' + str(unit_count)
|
||||
|
||||
if not 'services' in env_description:
|
||||
env_description['services'] = {}
|
||||
|
||||
if not 'webServers' in env_description['services']:
|
||||
env_description['services']['webServers'] = []
|
||||
|
||||
env_description['services']['webServers'].append(web_server)
|
||||
EnvironmentServices.save_environment_description(session_id,
|
||||
env_description)
|
||||
|
||||
return web_server
|
||||
|
||||
@staticmethod
|
||||
def delete_service(service_id, service_type, session_id, environment_id):
|
||||
env_description = EnvironmentServices.get_environment_description(
|
||||
environment_id, session_id)
|
||||
|
||||
if not 'services' in env_description:
|
||||
raise ValueError('This environment does not have services')
|
||||
|
||||
services = []
|
||||
if service_type in env_description['services']:
|
||||
services = env_description['services'][service_type]
|
||||
|
||||
services = [srv for srv in services if srv['id'] != service_id]
|
||||
env_description['services'][service_type] = services
|
||||
|
||||
EnvironmentServices.save_environment_description(session_id,
|
||||
env_description)
|
@ -14,6 +14,7 @@
|
||||
|
||||
import functools
|
||||
import logging
|
||||
from muranoapi.db.services.sessions import SessionServices, SessionState
|
||||
from webob import exc
|
||||
from muranoapi.db.models import Session
|
||||
from muranoapi.db.session import get_session
|
||||
@ -24,15 +25,22 @@ log = logging.getLogger(__name__)
|
||||
def verify_session(func):
|
||||
@functools.wraps(func)
|
||||
def __inner(self, request, *args, **kwargs):
|
||||
if hasattr(request, 'context') and request.context.session:
|
||||
uw = get_session().query(Session)
|
||||
configuration_session = uw.get(request.context.session)
|
||||
if not hasattr(request, 'context') and not request.context.session:
|
||||
log.info('Session is required for this call')
|
||||
raise exc.HTTPForbidden()
|
||||
|
||||
if configuration_session.state != 'open':
|
||||
log.info('Session is already deployed')
|
||||
raise exc.HTTPUnauthorized
|
||||
else:
|
||||
log.info('No session is supplied')
|
||||
raise exc.HTTPUnauthorized
|
||||
session_id = request.context.session
|
||||
|
||||
unit = get_session()
|
||||
session = unit.query(Session).get(session_id)
|
||||
|
||||
if not SessionServices.validate(session):
|
||||
log.info('Session <SessionId {0}> is invalid'.format(session_id))
|
||||
raise exc.HTTPForbidden()
|
||||
|
||||
if session.state == SessionState.deploying:
|
||||
log.info('Session <SessionId {0}> is already in '
|
||||
'deployment state'.format(session_id))
|
||||
raise exc.HTTPForbidden()
|
||||
return func(self, request, *args, **kwargs)
|
||||
return __inner
|
||||
|
Loading…
x
Reference in New Issue
Block a user