initial version

Change-Id: I699e0ab082657880998d8618fe29eb7f56c6c661
This commit is contained in:
David TARDIVEL
2015-06-04 15:26:55 +02:00
parent 073c6e49cb
commit d14e057da1
316 changed files with 27260 additions and 0 deletions

View File

View File

@@ -0,0 +1,54 @@
# A generic, single database configuration.
[alembic]
# path to migration scripts
script_location = %(here)s/alembic
# template used to generate migration files
# file_template = %%(rev)s_%%(slug)s
# max length of characters to apply to the
# "slug" field
#truncate_slug_length = 40
# set to 'true' to run the environment during
# the 'revision' command, regardless of autogenerate
# revision_environment = false
#sqlalchemy.url = driver://user:pass@localhost/dbname
# Logging configuration
[loggers]
keys = root,sqlalchemy,alembic
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = WARN
handlers = console
qualname =
[logger_sqlalchemy]
level = WARN
handlers =
qualname = sqlalchemy.engine
[logger_alembic]
level = INFO
handlers =
qualname = alembic
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic
[formatter_generic]
format = %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S

View File

@@ -0,0 +1,15 @@
Please see https://alembic.readthedocs.org/en/latest/index.html for general documentation
To create alembic migrations use:
$ watcher-db-manage revision --message "description of revision" --autogenerate
Stamp db with most recent migration version, without actually running migrations
$ watcher-db-manage stamp head
Upgrade can be performed by:
$ watcher-db-manage upgrade
$ watcher-db-manage upgrade head
Downgrading db:
$ watcher-db-manage downgrade
$ watcher-db-manage downgrade base

View File

@@ -0,0 +1,54 @@
# 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 logging import config as log_config
from alembic import context
from watcher.db.sqlalchemy import api as sqla_api
from watcher.db.sqlalchemy import models
# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
config = context.config
# Interpret the config file for Python logging.
# This line sets up loggers basically.
log_config.fileConfig(config.config_file_name)
# add your model's MetaData object here
# for 'autogenerate' support
# from myapp import mymodel
target_metadata = models.Base.metadata
# other values from the config, defined by the needs of env.py,
# can be acquired:
# my_important_option = config.get_main_option("my_important_option")
# ... etc.
def run_migrations_online():
"""Run migrations in 'online' mode.
In this scenario we need to create an Engine
and associate a connection with the context.
"""
engine = sqla_api.get_engine()
with engine.connect() as connection:
context.configure(connection=connection,
target_metadata=target_metadata)
with context.begin_transaction():
context.run_migrations()
run_migrations_online()

View File

@@ -0,0 +1,22 @@
"""${message}
Revision ID: ${up_revision}
Revises: ${down_revision}
Create Date: ${create_date}
"""
# revision identifiers, used by Alembic.
revision = ${repr(up_revision)}
down_revision = ${repr(down_revision)}
from alembic import op
import sqlalchemy as sa
${imports if imports else ""}
def upgrade():
${upgrades if upgrades else "pass"}
def downgrade():
${downgrades if downgrades else "pass"}

View File

@@ -0,0 +1,90 @@
"""Initial revision
Revision ID: 414bf1d36e7d
Revises: None
Create Date: 2015-04-08 15:05:50.942578
"""
# revision identifiers, used by Alembic.
revision = '414bf1d36e7d'
down_revision = None
from alembic import op
import sqlalchemy as sa
def upgrade():
### commands auto generated by Alembic - please adjust! ###
op.create_table('audit_templates',
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('deleted_at', sa.DateTime(), nullable=True),
sa.Column('deleted', sa.Integer(), nullable=True),
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('uuid', sa.String(length=36), nullable=True),
sa.Column('name', sa.String(length=63), nullable=True),
sa.Column('description', sa.String(length=255), nullable=True),
sa.Column('host_aggregate', sa.Integer(), nullable=True),
sa.Column('goal', sa.String(length=63), nullable=True),
sa.Column('extra', watcher.db.sqlalchemy.models.JSONEncodedDict(), nullable=True),
sa.Column('version', sa.String(length=15), nullable=True),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('name', name='uniq_audit_templates0name'),
sa.UniqueConstraint('uuid', name='uniq_audit_templates0uuid')
)
op.create_table('audits',
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('deleted_at', sa.DateTime(), nullable=True),
sa.Column('deleted', sa.Integer(), nullable=True),
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('uuid', sa.String(length=36), nullable=True),
sa.Column('type', sa.String(length=20), nullable=True),
sa.Column('state', sa.String(length=20), nullable=True),
sa.Column('deadline', sa.DateTime(), nullable=True),
sa.Column('audit_template_id', sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(['audit_template_id'], ['audit_templates.id'], ),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('uuid', name='uniq_audits0uuid')
)
op.create_table('action_plans',
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('deleted_at', sa.DateTime(), nullable=True),
sa.Column('deleted', sa.Integer(), nullable=True),
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('uuid', sa.String(length=36), nullable=True),
sa.Column('first_action_id', sa.Integer(), nullable=True),
sa.Column('audit_id', sa.Integer(), nullable=True),
sa.Column('state', sa.String(length=20), nullable=True),
sa.ForeignKeyConstraint(['audit_id'], ['audits.id'], ),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('uuid', name='uniq_action_plans0uuid')
)
op.create_table('actions',
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('deleted_at', sa.DateTime(), nullable=True),
sa.Column('deleted', sa.Integer(), nullable=True),
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('uuid', sa.String(length=36), nullable=True),
sa.Column('action_plan_id', sa.Integer(), nullable=True),
sa.Column('description', sa.String(length=255), nullable=True),
sa.Column('state', sa.String(length=20), nullable=True),
sa.Column('alarm', sa.String(length=36), nullable=True),
sa.Column('next', sa.String(length=36), nullable=True),
sa.ForeignKeyConstraint(['action_plan_id'], ['action_plans.id'], ),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('uuid', name='uniq_actions0uuid')
)
### end Alembic commands ###
def downgrade():
### commands auto generated by Alembic - please adjust! ###
op.drop_table('actions')
op.drop_table('action_plans')
op.drop_table('audits')
op.drop_table('audit_templates')
### end Alembic commands ###

View File

@@ -0,0 +1,617 @@
# -*- encoding: utf-8 -*-
#
# Copyright 2013 Hewlett-Packard Development Company, L.P.
#
# 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.
"""SQLAlchemy storage backend."""
from oslo_config import cfg
from oslo_db import exception as db_exc
from oslo_db.sqlalchemy import session as db_session
from oslo_db.sqlalchemy import utils as db_utils
from sqlalchemy.orm.exc import MultipleResultsFound
from sqlalchemy.orm.exc import NoResultFound
from watcher.common import exception
from watcher.common import utils
from watcher.db import api
from watcher.db.sqlalchemy import models
from watcher.objects.audit import AuditStatus
from watcher.openstack.common._i18n import _
from watcher.openstack.common import log
CONF = cfg.CONF
LOG = log.getLogger(__name__)
_FACADE = None
def _create_facade_lazily():
global _FACADE
if _FACADE is None:
_FACADE = db_session.EngineFacade.from_config(CONF)
return _FACADE
def get_engine():
facade = _create_facade_lazily()
return facade.get_engine()
def get_session(**kwargs):
facade = _create_facade_lazily()
return facade.get_session(**kwargs)
def get_backend():
"""The backend is this module itself."""
return Connection()
def model_query(model, *args, **kwargs):
"""Query helper for simpler session usage.
:param session: if present, the session to use
"""
session = kwargs.get('session') or get_session()
query = session.query(model, *args)
return query
def add_identity_filter(query, value):
"""Adds an identity filter to a query.
Filters results by ID, if supplied value is a valid integer.
Otherwise attempts to filter results by UUID.
:param query: Initial query to add filter to.
:param value: Value for filtering results by.
:return: Modified query.
"""
if utils.is_int_like(value):
return query.filter_by(id=value)
elif utils.is_uuid_like(value):
return query.filter_by(uuid=value)
else:
raise exception.InvalidIdentity(identity=value)
def _paginate_query(model, limit=None, marker=None, sort_key=None,
sort_dir=None, query=None):
if not query:
query = model_query(model)
sort_keys = ['id']
if sort_key and sort_key not in sort_keys:
sort_keys.insert(0, sort_key)
query = db_utils.paginate_query(query, model, limit, sort_keys,
marker=marker, sort_dir=sort_dir)
return query.all()
class Connection(api.Connection):
"""SqlAlchemy connection."""
def __init__(self):
pass
def _add_audit_templates_filters(self, query, filters):
if filters is None:
filters = []
if 'name' in filters:
query = query.filter_by(name=filters['name'])
if 'host_aggregate' in filters:
query = query.filter_by(host_aggregate=filters['host_aggregate'])
if 'goal' in filters:
query = query.filter_by(goal=filters['goal'])
return query
def _add_audits_filters(self, query, filters):
if filters is None:
filters = []
if 'type' in filters:
query = query.filter_by(type=filters['type'])
if 'state' in filters:
query = query.filter_by(state=filters['state'])
if 'audit_template_id' in filters:
query = query.filter_by(
audit_template_id=filters['audit_template_id'])
if 'audit_template_uuid' in filters:
query = query.join(
models.AuditTemplate,
models.Audit.audit_template_id == models.AuditTemplate.id)
query = query.filter(
models.AuditTemplate.uuid == filters['audit_template_uuid'])
if 'audit_template_name' in filters:
query = query.join(
models.AuditTemplate,
models.Audit.audit_template_id == models.AuditTemplate.id)
query = query.filter(
models.AuditTemplate.name ==
filters['audit_template_name'])
return query
def _add_action_plans_filters(self, query, filters):
if filters is None:
filters = []
if 'state' in filters:
query = query.filter_by(state=filters['state'])
if 'audit_id' in filters:
query = query.filter_by(audit_id=filters['audit_id'])
if 'audit_uuid' in filters:
query = query.join(models.Audit,
models.ActionPlan.audit_id == models.Audit.id)
query = query.filter(models.Audit.uuid == filters['audit_uuid'])
return query
def _add_actions_filters(self, query, filters):
if filters is None:
filters = []
if 'action_plan_id' in filters:
query = query.filter_by(action_plan_id=filters['action_plan_id'])
if 'action_plan_uuid' in filters:
query = query.join(
models.ActionPlan,
models.Action.action_plan_id == models.ActionPlan.id)
query = query.filter(
models.ActionPlan.uuid == filters['action_plan_uuid'])
if 'audit_uuid' in filters:
stmt = model_query(models.ActionPlan).join(
models.Audit,
models.Audit.id == models.ActionPlan.audit_id)\
.filter_by(uuid=filters['audit_uuid']).subquery()
query = query.filter_by(action_plan_id=stmt.c.id)
if 'state' in filters:
query = query.filter_by(state=filters['state'])
if 'alarm' in filters:
query = query.filter_by(alarm=filters['alarm'])
return query
def get_audit_template_list(self, context, filters=None, limit=None,
marker=None, sort_key=None, sort_dir=None):
query = model_query(models.AuditTemplate)
query = self._add_audit_templates_filters(query, filters)
if not context.show_deleted:
query = query.filter_by(deleted_at=None)
return _paginate_query(models.AuditTemplate, limit, marker,
sort_key, sort_dir, query)
def create_audit_template(self, values):
# ensure defaults are present for new audit_templates
if not values.get('uuid'):
values['uuid'] = utils.generate_uuid()
audit_template = models.AuditTemplate()
audit_template.update(values)
try:
audit_template.save()
except db_exc.DBDuplicateEntry:
raise exception.AuditTemplateAlreadyExists(uuid=values['uuid'],
name=values['name'])
return audit_template
def get_audit_template_by_id(self, context, audit_template_id):
query = model_query(models.AuditTemplate)
query = query.filter_by(id=audit_template_id)
try:
audit_template = query.one()
if not context.show_deleted:
if audit_template.deleted_at is not None:
raise exception.AuditTemplateNotFound(
audit_template=audit_template_id)
return audit_template
except NoResultFound:
raise exception.AuditTemplateNotFound(
audit_template=audit_template_id)
def get_audit_template_by_uuid(self, context, audit_template_uuid):
query = model_query(models.AuditTemplate)
query = query.filter_by(uuid=audit_template_uuid)
try:
audit_template = query.one()
if not context.show_deleted:
if audit_template.deleted_at is not None:
raise exception.AuditTemplateNotFound(
audit_template=audit_template_uuid)
return audit_template
except NoResultFound:
raise exception.AuditTemplateNotFound(
audit_template=audit_template_uuid)
def get_audit_template_by_name(self, context, audit_template_name):
query = model_query(models.AuditTemplate)
query = query.filter_by(name=audit_template_name)
try:
audit_template = query.one()
if not context.show_deleted:
if audit_template.deleted_at is not None:
raise exception.AuditTemplateNotFound(
audit_template=audit_template_name)
return audit_template
except MultipleResultsFound:
raise exception.Conflict(
'Multiple audit templates exist with same name.'
' Please use the audit template uuid instead.')
except NoResultFound:
raise exception.AuditTemplateNotFound(
audit_template=audit_template_name)
def destroy_audit_template(self, audit_template_id):
session = get_session()
with session.begin():
query = model_query(models.AuditTemplate, session=session)
query = add_identity_filter(query, audit_template_id)
try:
query.one()
except NoResultFound:
raise exception.AuditTemplateNotFound(node=audit_template_id)
query.delete()
def update_audit_template(self, audit_template_id, values):
if 'uuid' in values:
msg = _("Cannot overwrite UUID for an existing AuditTemplate.")
raise exception.InvalidParameterValue(err=msg)
return self._do_update_audit_template(audit_template_id, values)
def _do_update_audit_template(self, audit_template_id, values):
session = get_session()
with session.begin():
query = model_query(models.AuditTemplate, session=session)
query = add_identity_filter(query, audit_template_id)
try:
ref = query.with_lockmode('update').one()
except NoResultFound:
raise exception.AuditTemplateNotFound(
audit_template=audit_template_id)
ref.update(values)
return ref
def soft_delete_audit_template(self, audit_template_id):
session = get_session()
with session.begin():
query = model_query(models.AuditTemplate, session=session)
query = add_identity_filter(query, audit_template_id)
try:
query.one()
except NoResultFound:
raise exception.AuditTemplateNotFound(node=audit_template_id)
query.soft_delete()
def get_audit_list(self, context, filters=None, limit=None, marker=None,
sort_key=None, sort_dir=None):
query = model_query(models.Audit)
query = self._add_audits_filters(query, filters)
if not context.show_deleted:
query = query.filter(~(models.Audit.state == 'DELETED'))
return _paginate_query(models.Audit, limit, marker,
sort_key, sort_dir, query)
def create_audit(self, values):
# ensure defaults are present for new audits
if not values.get('uuid'):
values['uuid'] = utils.generate_uuid()
if values.get('state') is None:
values['state'] = AuditStatus.PENDING
audit = models.Audit()
audit.update(values)
try:
audit.save()
except db_exc.DBDuplicateEntry:
raise exception.AuditAlreadyExists(uuid=values['uuid'])
return audit
def get_audit_by_id(self, context, audit_id):
query = model_query(models.Audit)
query = query.filter_by(id=audit_id)
try:
audit = query.one()
if not context.show_deleted:
if audit.state == 'DELETED':
raise exception.AuditNotFound(audit=audit_id)
return audit
except NoResultFound:
raise exception.AuditNotFound(audit=audit_id)
def get_audit_by_uuid(self, context, audit_uuid):
query = model_query(models.Audit)
query = query.filter_by(uuid=audit_uuid)
try:
audit = query.one()
if not context.show_deleted:
if audit.state == 'DELETED':
raise exception.AuditNotFound(audit=audit_uuid)
return audit
except NoResultFound:
raise exception.AuditNotFound(audit=audit_uuid)
def destroy_audit(self, audit_id):
def is_audit_referenced(session, audit_id):
"""Checks whether the audit is referenced by action_plan(s)."""
query = model_query(models.ActionPlan, session=session)
query = self._add_action_plans_filters(
query, {'audit_id': audit_id})
return query.count() != 0
session = get_session()
with session.begin():
query = model_query(models.Audit, session=session)
query = add_identity_filter(query, audit_id)
try:
audit_ref = query.one()
except NoResultFound:
raise exception.AuditNotFound(audit=audit_id)
if is_audit_referenced(session, audit_ref['id']):
raise exception.AuditReferenced(audit=audit_id)
query.delete()
def update_audit(self, audit_id, values):
if 'uuid' in values:
msg = _("Cannot overwrite UUID for an existing Audit.")
raise exception.InvalidParameterValue(err=msg)
return self._do_update_audit(audit_id, values)
def _do_update_audit(self, audit_id, values):
session = get_session()
with session.begin():
query = model_query(models.Audit, session=session)
query = add_identity_filter(query, audit_id)
try:
ref = query.with_lockmode('update').one()
except NoResultFound:
raise exception.AuditNotFound(audit=audit_id)
ref.update(values)
return ref
def soft_delete_audit(self, audit_id):
session = get_session()
with session.begin():
query = model_query(models.Audit, session=session)
query = add_identity_filter(query, audit_id)
try:
query.one()
except NoResultFound:
raise exception.AuditNotFound(node=audit_id)
query.soft_delete()
def get_action_list(self, context, filters=None, limit=None, marker=None,
sort_key=None, sort_dir=None):
query = model_query(models.Action)
query = self._add_actions_filters(query, filters)
if not context.show_deleted:
query = query.filter(~(models.Action.state == 'DELETED'))
return _paginate_query(models.Action, limit, marker,
sort_key, sort_dir, query)
def create_action(self, values):
# ensure defaults are present for new actions
if not values.get('uuid'):
values['uuid'] = utils.generate_uuid()
action = models.Action()
action.update(values)
try:
action.save()
except db_exc.DBDuplicateEntry:
raise exception.ActionAlreadyExists(uuid=values['uuid'])
return action
def get_action_by_id(self, context, action_id):
query = model_query(models.Action)
query = query.filter_by(id=action_id)
try:
action = query.one()
if not context.show_deleted:
if action.state == 'DELETED':
raise exception.ActionNotFound(
action=action_id)
return action
except NoResultFound:
raise exception.ActionNotFound(action=action_id)
def get_action_by_uuid(self, context, action_uuid):
query = model_query(models.Action)
query = query.filter_by(uuid=action_uuid)
try:
action = query.one()
if not context.show_deleted:
if action.state == 'DELETED':
raise exception.ActionNotFound(
action=action_uuid)
return action
except NoResultFound:
raise exception.ActionNotFound(action=action_uuid)
def destroy_action(self, action_id):
session = get_session()
with session.begin():
query = model_query(models.Action, session=session)
query = add_identity_filter(query, action_id)
count = query.delete()
if count != 1:
raise exception.ActionNotFound(action_id)
def update_action(self, action_id, values):
# NOTE(dtantsur): this can lead to very strange errors
if 'uuid' in values:
msg = _("Cannot overwrite UUID for an existing Action.")
raise exception.InvalidParameterValue(err=msg)
return self._do_update_action(action_id, values)
def _do_update_action(self, action_id, values):
session = get_session()
with session.begin():
query = model_query(models.Action, session=session)
query = add_identity_filter(query, action_id)
try:
ref = query.with_lockmode('update').one()
except NoResultFound:
raise exception.ActionNotFound(action=action_id)
ref.update(values)
return ref
def soft_delete_action(self, action_id):
session = get_session()
with session.begin():
query = model_query(models.Action, session=session)
query = add_identity_filter(query, action_id)
try:
query.one()
except NoResultFound:
raise exception.ActionNotFound(node=action_id)
query.soft_delete()
def get_action_plan_list(
self, context, columns=None, filters=None, limit=None,
marker=None, sort_key=None, sort_dir=None):
query = model_query(models.ActionPlan)
query = self._add_action_plans_filters(query, filters)
if not context.show_deleted:
query = query.filter(~(models.ActionPlan.state == 'DELETED'))
return _paginate_query(models.ActionPlan, limit, marker,
sort_key, sort_dir, query)
def create_action_plan(self, values):
# ensure defaults are present for new audits
if not values.get('uuid'):
values['uuid'] = utils.generate_uuid()
action_plan = models.ActionPlan()
action_plan.update(values)
try:
action_plan.save()
except db_exc.DBDuplicateEntry:
raise exception.ActionPlanAlreadyExists(uuid=values['uuid'])
return action_plan
def get_action_plan_by_id(self, context, action_plan_id):
query = model_query(models.ActionPlan)
query = query.filter_by(id=action_plan_id)
try:
action_plan = query.one()
if not context.show_deleted:
if action_plan.state == 'DELETED':
raise exception.ActionPlanNotFound(
action_plan=action_plan_id)
return action_plan
except NoResultFound:
raise exception.ActionPlanNotFound(action_plan=action_plan_id)
def get_action_plan_by_uuid(self, context, action_plan__uuid):
query = model_query(models.ActionPlan)
query = query.filter_by(uuid=action_plan__uuid)
try:
action_plan = query.one()
if not context.show_deleted:
if action_plan.state == 'DELETED':
raise exception.ActionPlanNotFound(
action_plan=action_plan__uuid)
return action_plan
except NoResultFound:
raise exception.ActionPlanNotFound(action_plan=action_plan__uuid)
def destroy_action_plan(self, action_plan_id):
def is_action_plan_referenced(session, action_plan_id):
"""Checks whether the action_plan is referenced by action(s)."""
query = model_query(models.Action, session=session)
query = self._add_actions_filters(
query, {'action_plan_id': action_plan_id})
return query.count() != 0
session = get_session()
with session.begin():
query = model_query(models.ActionPlan, session=session)
query = add_identity_filter(query, action_plan_id)
try:
action_plan_ref = query.one()
except NoResultFound:
raise exception.ActionPlanNotFound(action_plan=action_plan_id)
if is_action_plan_referenced(session, action_plan_ref['id']):
raise exception.ActionPlanReferenced(
action_plan=action_plan_id)
query.delete()
def update_action_plan(self, action_plan_id, values):
if 'uuid' in values:
msg = _("Cannot overwrite UUID for an existing Audit.")
raise exception.InvalidParameterValue(err=msg)
return self._do_update_action_plan(action_plan_id, values)
def _do_update_action_plan(self, action_plan_id, values):
session = get_session()
with session.begin():
query = model_query(models.ActionPlan, session=session)
query = add_identity_filter(query, action_plan_id)
try:
ref = query.with_lockmode('update').one()
except NoResultFound:
raise exception.ActionPlanNotFound(action_plan=action_plan_id)
ref.update(values)
return ref
def soft_delete_action_plan(self, action_plan_id):
session = get_session()
with session.begin():
query = model_query(models.ActionPlan, session=session)
query = add_identity_filter(query, action_plan_id)
try:
query.one()
except NoResultFound:
raise exception.ActionPlanNotFound(node=action_plan_id)
query.soft_delete()

View File

@@ -0,0 +1,113 @@
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# 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 os
import alembic
from alembic import config as alembic_config
import alembic.migration as alembic_migration
from oslo_db import exception as db_exc
from watcher.db.sqlalchemy import api as sqla_api
from watcher.db.sqlalchemy import models
def _alembic_config():
path = os.path.join(os.path.dirname(__file__), 'alembic.ini')
config = alembic_config.Config(path)
return config
def version(config=None, engine=None):
"""Current database version.
:returns: Database version
:rtype: string
"""
if engine is None:
engine = sqla_api.get_engine()
with engine.connect() as conn:
context = alembic_migration.MigrationContext.configure(conn)
return context.get_current_revision()
def upgrade(revision, config=None):
"""Used for upgrading database.
:param version: Desired database version
:type version: string
"""
revision = revision or 'head'
config = config or _alembic_config()
alembic.command.upgrade(config, revision or 'head')
def create_schema(config=None, engine=None):
"""Create database schema from models description.
Can be used for initial installation instead of upgrade('head').
"""
if engine is None:
engine = sqla_api.get_engine()
# NOTE(viktors): If we will use metadata.create_all() for non empty db
# schema, it will only add the new tables, but leave
# existing as is. So we should avoid of this situation.
if version(engine=engine) is not None:
raise db_exc.DbMigrationError("DB schema is already under version"
" control. Use upgrade() instead")
models.Base.metadata.create_all(engine)
stamp('head', config=config)
def downgrade(revision, config=None):
"""Used for downgrading database.
:param version: Desired database version
:type version: string
"""
revision = revision or 'base'
config = config or _alembic_config()
return alembic.command.downgrade(config, revision)
def stamp(revision, config=None):
"""Stamps database with provided revision.
Don't run any migrations.
:param revision: Should match one from repository or head - to stamp
database with most recent revision
:type revision: string
"""
config = config or _alembic_config()
return alembic.command.stamp(config, revision=revision)
def revision(message=None, autogenerate=False, config=None):
"""Creates template for migration.
:param message: Text that will be used for migration title
:type message: string
:param autogenerate: If True - generates diff based on current database
state
:type autogenerate: bool
"""
config = config or _alembic_config()
return alembic.command.revision(config, message=message,
autogenerate=autogenerate)

View File

@@ -0,0 +1,189 @@
# Copyright 2013 Hewlett-Packard Development Company, L.P.
#
# 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.
"""
SQLAlchemy models for watcher service
"""
import json
from oslo_config import cfg
from oslo_db import options as db_options
from oslo_db.sqlalchemy import models
import six.moves.urllib.parse as urlparse
from sqlalchemy import Column
from sqlalchemy import DateTime
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import ForeignKey
from sqlalchemy import Integer
from sqlalchemy import schema
from sqlalchemy import String
from sqlalchemy.types import TypeDecorator, TEXT
from watcher.common import paths
sql_opts = [
cfg.StrOpt('mysql_engine',
default='InnoDB',
help='MySQL engine to use.')
]
_DEFAULT_SQL_CONNECTION = 'sqlite:///' + paths.state_path_def('watcher.sqlite')
cfg.CONF.register_opts(sql_opts, 'database')
db_options.set_defaults(cfg.CONF, _DEFAULT_SQL_CONNECTION, 'watcher.sqlite')
def table_args():
engine_name = urlparse.urlparse(cfg.CONF.database.connection).scheme
if engine_name == 'mysql':
return {'mysql_engine': cfg.CONF.database.mysql_engine,
'mysql_charset': "utf8"}
return None
class JsonEncodedType(TypeDecorator):
"""Abstract base type serialized as json-encoded string in db."""
type = None
impl = TEXT
def process_bind_param(self, value, dialect):
if value is None:
# Save default value according to current type to keep the
# interface the consistent.
value = self.type()
elif not isinstance(value, self.type):
raise TypeError("%s supposes to store %s objects, but %s given"
% (self.__class__.__name__,
self.type.__name__,
type(value).__name__))
serialized_value = json.dumps(value)
return serialized_value
def process_result_value(self, value, dialect):
if value is not None:
value = json.loads(value)
return value
class JSONEncodedDict(JsonEncodedType):
"""Represents dict serialized as json-encoded string in db."""
type = dict
class JSONEncodedList(JsonEncodedType):
"""Represents list serialized as json-encoded string in db."""
type = list
class WatcherBase(models.SoftDeleteMixin,
models.TimestampMixin, models.ModelBase):
metadata = None
def as_dict(self):
d = {}
for c in self.__table__.columns:
d[c.name] = self[c.name]
return d
def save(self, session=None):
import watcher.db.sqlalchemy.api as db_api
if session is None:
session = db_api.get_session()
super(WatcherBase, self).save(session)
Base = declarative_base(cls=WatcherBase)
class AuditTemplate(Base):
"""Represents an audit template."""
__tablename__ = 'audit_templates'
__table_args__ = (
schema.UniqueConstraint('uuid', name='uniq_audit_templates0uuid'),
schema.UniqueConstraint('name', name='uniq_audit_templates0name'),
table_args()
)
id = Column(Integer, primary_key=True)
uuid = Column(String(36))
name = Column(String(63), nullable=True)
description = Column(String(255), nullable=True)
host_aggregate = Column(Integer, nullable=True)
goal = Column(String(63), nullable=True)
extra = Column(JSONEncodedDict)
version = Column(String(15), nullable=True)
class Audit(Base):
"""Represents an audit."""
__tablename__ = 'audits'
__table_args__ = (
schema.UniqueConstraint('uuid', name='uniq_audits0uuid'),
table_args()
)
id = Column(Integer, primary_key=True)
uuid = Column(String(36))
type = Column(String(20))
state = Column(String(20), nullable=True)
deadline = Column(DateTime, nullable=True)
audit_template_id = Column(Integer, ForeignKey('audit_templates.id'),
nullable=False)
class Action(Base):
"""Represents an action."""
__tablename__ = 'actions'
__table_args__ = (
schema.UniqueConstraint('uuid', name='uniq_actions0uuid'),
table_args()
)
id = Column(Integer, primary_key=True)
uuid = Column(String(36))
action_plan_id = Column(Integer, ForeignKey('action_plans.id'),
nullable=True)
# only for the first version
action_type = Column(String(255))
applies_to = Column(String(255))
src = Column(String(255))
dst = Column(String(255))
parameter = Column(String(255))
description = Column(String(255))
state = Column(String(20), nullable=True)
alarm = Column(String(36))
next = Column(String(36), nullable=True)
class ActionPlan(Base):
"""Represents an action plan."""
__tablename__ = 'action_plans'
__table_args__ = (
schema.UniqueConstraint('uuid', name='uniq_action_plans0uuid'),
table_args()
)
id = Column(Integer, primary_key=True)
uuid = Column(String(36))
first_action_id = Column(Integer)
# first_action_id = Column(Integer, ForeignKeyConstraint(
# ['first_action_id'], ['actions.id'], name='fk_first_action_id'),
# nullable=True)
audit_id = Column(Integer, ForeignKey('audits.id'),
nullable=True)
state = Column(String(20), nullable=True)