Deployment logs fetching

Added Deployment instance to group, store and fetch deployment logs (status event) by the deployment attempts

Change-Id: I5d31da1140c3fcf9b72322093a5569ce54b36aaf
This commit is contained in:
Alexander Tivelkov 2013-07-11 17:45:52 +04:00
parent eb1cc1630a
commit 4bf7e6d45e
9 changed files with 250 additions and 75 deletions

View File

@ -0,0 +1,62 @@
# 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 muranoapi.openstack.common import wsgi
from muranoapi.db.models import Deployment, Status
from muranoapi.db.session import get_session
from muranoapi.openstack.common import log as logging
from sqlalchemy import desc
log = logging.getLogger(__name__)
class Controller(object):
def index(self, request, environment_id):
unit = get_session()
query = unit.query(Deployment) \
.filter_by(environment_id=environment_id) \
.order_by(desc(Deployment.created))
result = query.all()
deployments = [deployment.to_dict() for deployment in result]
return {'deployments': deployments}
def statuses(self, request, environment_id, deployment_id):
unit = get_session()
query = unit.query(Status) \
.filter_by(deployment_id=deployment_id) \
.order_by(Status.created)
if 'service_id' in request.GET:
service_id_set = set(request.GET.getall('service_id'))
environment = unit.query(Deployment).get(deployment_id).description
entity_ids = []
if 'services' in environment:
for service in environment['services']:
if service['id'] in service_id_set:
entity_ids.append(service['id'])
if 'units' in service:
unit_ids = [u['id'] for u in service['units']
if 'id' in u]
entity_ids = entity_ids + unit_ids
if entity_ids:
query = query.filter(Status.entity_id.in_(entity_ids))
else:
return {'reports': None}
result = query.all()
return {'reports': [status.to_dict() for status in result]}
def create_resource():
return wsgi.Resource(Controller())

View File

@ -14,7 +14,7 @@
import routes
from muranoapi.openstack.common import wsgi
from muranoapi.api.v1 import environments, services
from muranoapi.api.v1 import environments, services, deployments
from muranoapi.api.v1 import sessions
@ -83,6 +83,17 @@ class API(wsgi.Router):
action='delete',
conditions={'method': ['DELETE']})
deployments_resource = deployments.create_resource()
mapper.connect('/environments/{environment_id}/deployments',
controller=deployments_resource,
action='index',
conditions={'method': ['GET']})
mapper.connect('/environments/{environment_id}/deployments/'
'{deployment_id}',
controller=deployments_resource,
action='statuses',
conditions={'method': ['GET']})
sessions_resource = sessions.create_resource()
mapper.connect('/environments/{environment_id}/configure',
controller=sessions_resource,
@ -96,11 +107,6 @@ class API(wsgi.Router):
controller=sessions_resource,
action='delete',
conditions={'method': ['DELETE']})
mapper.connect('/environments/{environment_id}/sessions/'
'{session_id}/reports',
controller=sessions_resource,
action='reports',
conditions={'method': ['GET']})
mapper.connect('/environments/{environment_id}/sessions/'
'{session_id}/deploy',
controller=sessions_resource,

View File

@ -13,7 +13,7 @@
# under the License.
from webob import exc
from muranoapi.db.models import Session, Status
from muranoapi.db.models import Session
from muranoapi.db.session import get_session
from muranoapi.db.services.sessions import SessionServices
from muranoapi.db.services.sessions import SessionState
@ -114,52 +114,6 @@ class Controller(object):
SessionServices.deploy(session, unit, 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)))
unit = get_session()
statuses = unit.query(Status).filter_by(session_id=session_id).all()
result = statuses
if 'service_id' in request.GET:
service_id = request.GET['service_id']
environment = unit.query(Session).get(session_id).description
services = []
if 'services' in environment and 'activeDirectories' in \
environment['services']:
services += environment['services']['activeDirectories']
if 'services' in environment and 'webServers' in \
environment['services']:
services += environment['services']['webServers']
if 'services' in environment and 'aspNetApps' in\
environment['services']:
services += environment['services']['aspNetApps']
if 'services' in environment and 'webServerFarms' in \
environment['services']:
services += environment['services']['webServerFarms']
if 'services' in environment and 'aspNetAppFarms' in\
environment['services']:
services += environment['services']['aspNetAppFarms']
service = [service for service in services
if service['id'] == service_id][0]
if service:
entities = [u['id'] for u in service['units']]
entities.append(service_id)
result = []
for status in statuses:
if status.entity_id in entities:
result.append(status)
return {'reports': [status.to_dict() for status in result]}
def create_resource():
return wsgi.Resource(Controller())

View File

@ -18,10 +18,11 @@ from amqplib.client_0_8 import AMQPConnectionException
import anyjson
import eventlet
from muranoapi.common.utils import retry, handle
from muranoapi.db.models import Status, Session, Environment
from muranoapi.db.models import Status, Session, Environment, Deployment
from muranoapi.db.session import get_session
from muranoapi.openstack.common import log as logging
from muranoapi.openstack.common import log as logging, timeutils
from muranoapi.common import config
from sqlalchemy import desc
amqp = eventlet.patcher.import_patched('amqplib.client_0_8')
conf = config.CONF.reports
@ -99,6 +100,14 @@ def handle_result(msg):
conf_session.state = 'deployed'
conf_session.save(session)
#close deployment
deployment = get_last_deployment(session, environment.id)
deployment.finished = timeutils.utcnow()
status = Status()
status.deployment_id = deployment.id
status.text = "Deployment finished"
deployment.statuses.append(status)
deployment.save(session)
msg.channel.basic_ack(msg.delivery_tag)
@ -115,11 +124,16 @@ def handle_report(msg):
status.update(params)
session = get_session()
#connect with session
conf_session = session.query(Session).filter_by(
**{'environment_id': status.environment_id,
'state': 'deploying'}).first()
status.session_id = conf_session.id
#connect with deployment
with session.begin():
running_deployment = get_last_deployment(session,
status.environment_id)
status.deployment_id = running_deployment.id
session.add(status)
def get_last_deployment(session, env_id):
query = session.query(Deployment). \
filter_by(environment_id=env_id). \
order_by(desc(Deployment.started))
return query.first()

View File

@ -0,0 +1,38 @@
# 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, ForeignKey
from sqlalchemy.types import String, DateTime
meta = MetaData()
deployment = Table('deployment', meta,
Column('id', String(32), primary_key=True),
Column('environment_id', String(32),
ForeignKey('environment.id')),
Column('created', DateTime, nullable=False),
Column('updated', DateTime, nullable=False),
Column('started', DateTime, nullable=False),
Column('finished', DateTime, nullable=True))
def upgrade(migrate_engine):
meta.bind = migrate_engine
meta.reflect()
deployment.create()
def downgrade(migrate_engine):
meta.bind = migrate_engine
deployment.drop()

View File

@ -0,0 +1,43 @@
# 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, ForeignKey
from sqlalchemy.types import String
meta = MetaData()
def upgrade(migrate_engine):
meta.bind = migrate_engine
meta.reflect()
status = Table('status', meta, autoload=True)
deployment_id = Column('deployment_id', String(32),
ForeignKey('deployment.id'))
deployment_id.create(status)
status.c.environment_id.drop()
status.c.session_id.drop()
status.c.entity.alter(nullable=True)
def downgrade(migrate_engine):
meta.bind = migrate_engine
meta.reflect()
status = Table('status', meta, autoload=True)
status.c.deployment_id.drop()
environment_id = Column('environment_id', String(32),
ForeignKey('environment.id'))
environment_id.create(status)
session_id = Column('session_id', String(32), ForeignKey('session.id'))
session_id.create(status)
status.c.entity.alter(nullable=False)

View File

@ -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 Text
meta = MetaData()
def upgrade(migrate_engine):
meta.bind = migrate_engine
deployment = Table('deployment', meta, autoload=True)
description = Column('description', Text(), nullable=True, default='{}')
description.create(deployment)
def downgrade(migrate_engine):
meta.bind = migrate_engine
deployment = Table('deployment', meta, autoload=True)
deployment.c.description.drop()

View File

@ -107,8 +107,8 @@ class Environment(BASE, ModelBase):
sessions = relationship("Session", backref='environment',
cascade='save-update, merge, delete')
statuses = relationship("Status", backref='environment',
cascade='save-update, merge, delete')
deployments = relationship("Deployment", backref='environment',
cascade='save-update, merge, delete')
def to_dict(self):
dictionary = super(Environment, self).to_dict()
@ -136,23 +136,42 @@ class Session(BASE, ModelBase):
return dictionary
class Deployment(BASE, ModelBase):
__tablename__ = 'deployment'
id = Column(String(32), primary_key=True, default=uuidutils.generate_uuid)
started = Column(DateTime, default=timeutils.utcnow, nullable=False)
finished = Column(DateTime, default=None, nullable=True)
description = Column(JsonBlob(), nullable=False)
environment_id = Column(String(32), ForeignKey('environment.id'))
statuses = relationship("Status", backref='deployment',
cascade='save-update, merge, delete')
def to_dict(self):
dictionary = super(Deployment, self).to_dict()
del dictionary["description"]
if 'statuses' in dictionary:
del dictionary['statuses']
if 'environment' in dictionary:
del dictionary['environment']
return dictionary
class Status(BASE, ModelBase):
__tablename__ = 'status'
id = Column(String(32), primary_key=True, default=uuidutils.generate_uuid)
entity_id = Column(String(32), nullable=False)
entity = Column(String(10), nullable=False)
environment_id = Column(String(32), ForeignKey('environment.id'))
session_id = Column(String(32), ForeignKey('session.id'))
entity_id = Column(String(32), nullable=True)
entity = Column(String(10), nullable=True)
deployment_id = Column(String(32), ForeignKey('deployment.id'))
text = Column(Text(), nullable=False)
def to_dict(self):
dictionary = super(Status, self).to_dict()
#object relations may be not loaded yet
if 'session' in dictionary:
del dictionary['session']
if 'environment' in dictionary:
del dictionary['environment']
if 'deployment' in dictionary:
del dictionary['deployment']
return dictionary
@ -160,7 +179,7 @@ def register_models(engine):
"""
Creates database tables for all models with the given engine
"""
models = (Environment, Status, Session)
models = (Environment, Status, Session, Deployment)
for model in models:
model.metadata.create_all(engine)
@ -169,6 +188,6 @@ def unregister_models(engine):
"""
Drops database tables for all models with the given engine
"""
models = (Environment, Status, Session)
models = (Environment, Status, Session, Deployment)
for model in models:
model.metadata.drop_all(engine)

View File

@ -17,7 +17,7 @@ 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.models import Session, Environment, Deployment, Status
from muranoapi.db.session import get_session
@ -126,7 +126,15 @@ class SessionServices(object):
environment['token'] = token
session.state = SessionState.deploying
session.save(unit)
deployment = Deployment()
deployment.environment_id = environment['id']
deployment.description = session.description
status = Status()
status.text = "Deployment scheduled"
deployment.statuses.append(status)
with unit.begin():
unit.add(session)
unit.add(deployment)
connection = amqp.Connection('{0}:{1}'.
format(rabbitmq.host, rabbitmq.port),