New data type to support Json lists

The previous Json data type has flaws support Json list. This patch adds
new data type to support lists.  It also removes the LongText data type
which is not very useful in Senlin.
This commit is contained in:
tengqm 2015-01-23 11:18:55 +08:00
parent 732710eee8
commit 7fb05e1f99
4 changed files with 88 additions and 59 deletions

View File

@ -25,9 +25,9 @@ def upgrade(migrate_engine):
primary_key=True, nullable=False),
sqlalchemy.Column('name', sqlalchemy.String(255)),
sqlalchemy.Column('type', sqlalchemy.String(255)),
sqlalchemy.Column('spec', types.Json),
sqlalchemy.Column('spec', types.Dict),
sqlalchemy.Column('permission', sqlalchemy.String(32)),
sqlalchemy.Column('tags', types.Json),
sqlalchemy.Column('tags', types.Dict),
sqlalchemy.Column('created_time', sqlalchemy.DateTime),
sqlalchemy.Column('updated_time', sqlalchemy.DateTime),
sqlalchemy.Column('deleted_time', sqlalchemy.DateTime),
@ -56,8 +56,8 @@ def upgrade(migrate_engine):
sqlalchemy.Column('timeout', sqlalchemy.Integer),
sqlalchemy.Column('status', sqlalchemy.String(255)),
sqlalchemy.Column('status_reason', sqlalchemy.String(255)),
sqlalchemy.Column('tags', types.Json),
sqlalchemy.Column('data', types.Json),
sqlalchemy.Column('tags', types.Dict),
sqlalchemy.Column('data', types.Dict),
mysql_engine='InnoDB',
mysql_charset='utf8'
)
@ -80,8 +80,8 @@ def upgrade(migrate_engine):
sqlalchemy.Column('deleted_time', sqlalchemy.DateTime),
sqlalchemy.Column('status', sqlalchemy.String(255)),
sqlalchemy.Column('status_reason', sqlalchemy.String(255)),
sqlalchemy.Column('tags', types.Json),
sqlalchemy.Column('data', types.Json),
sqlalchemy.Column('tags', types.Dict),
sqlalchemy.Column('data', types.Dict),
mysql_engine='InnoDB',
mysql_charset='utf8'
)
@ -115,8 +115,8 @@ def upgrade(migrate_engine):
sqlalchemy.Column('cooldown', sqlalchemy.Integer),
sqlalchemy.Column('level', sqlalchemy.Integer),
sqlalchemy.Column('deleted_time', sqlalchemy.DateTime),
sqlalchemy.Column('spec', types.Json),
sqlalchemy.Column('data', types.Json),
sqlalchemy.Column('spec', types.Dict),
sqlalchemy.Column('data', types.Dict),
mysql_engine='InnoDB',
mysql_charset='utf8'
)
@ -143,9 +143,9 @@ def upgrade(migrate_engine):
sqlalchemy.Column('id', sqlalchemy.String(36),
primary_key=True, nullable=False),
sqlalchemy.Column('name', sqlalchemy.String(63)),
sqlalchemy.Column('context', types.Json),
sqlalchemy.Column('context', types.Dict),
sqlalchemy.Column('target', sqlalchemy.String(36)),
sqlalchemy.Column('action', types.LongText),
sqlalchemy.Column('action', sqlalchemy.Text),
sqlalchemy.Column('cause', sqlalchemy.String(255)),
sqlalchemy.Column('owner', sqlalchemy.String(36)),
sqlalchemy.Column('interval', sqlalchemy.Integer),
@ -155,10 +155,10 @@ def upgrade(migrate_engine):
sqlalchemy.Column('control', sqlalchemy.String(255)),
sqlalchemy.Column('status', sqlalchemy.String(255)),
sqlalchemy.Column('status_reason', sqlalchemy.String(255)),
sqlalchemy.Column('inputs', types.Json),
sqlalchemy.Column('outputs', types.Json),
sqlalchemy.Column('depends_on', types.Json),
sqlalchemy.Column('depended_by', types.Json),
sqlalchemy.Column('inputs', types.Dict),
sqlalchemy.Column('outputs', types.Dict),
sqlalchemy.Column('depends_on', types.List),
sqlalchemy.Column('depended_by', types.List),
sqlalchemy.Column('deleted_time', sqlalchemy.DateTime),
mysql_engine='InnoDB',
mysql_charset='utf8'
@ -178,7 +178,7 @@ def upgrade(migrate_engine):
sqlalchemy.Column('action', sqlalchemy.String(36)),
sqlalchemy.Column('status', sqlalchemy.String(255)),
sqlalchemy.Column('status_reason', sqlalchemy.String(255)),
sqlalchemy.Column('tags', types.Json),
sqlalchemy.Column('tags', types.Dict),
mysql_engine='InnoDB',
mysql_charset='utf8'
)

View File

@ -12,7 +12,7 @@
import os
from oslo.db.sqlalchemy import migration as oslo_migration
from oslo_db.sqlalchemy import migration as oslo_migration
INIT_VERSION = 0

View File

@ -16,7 +16,7 @@ SQLAlchemy models for Senlin data.
import uuid
from oslo.db.sqlalchemy import models
from oslo_db.sqlalchemy import models
from oslo_utils import timeutils
import six
import sqlalchemy
@ -103,8 +103,8 @@ class Cluster(BASE, SenlinBase, SoftDelete):
timeout = sqlalchemy.Column(sqlalchemy.Integer)
status = sqlalchemy.Column(sqlalchemy.String(255))
status_reason = sqlalchemy.Column(sqlalchemy.String(255))
tags = sqlalchemy.Column(types.Json)
data = sqlalchemy.Column(types.Json)
tags = sqlalchemy.Column(types.Dict)
data = sqlalchemy.Column(types.Dict)
class Node(BASE, SenlinBase, SoftDelete):
@ -129,8 +129,8 @@ class Node(BASE, SenlinBase, SoftDelete):
status = sqlalchemy.Column(sqlalchemy.String(255))
status_reason = sqlalchemy.Column(sqlalchemy.String(255))
tags = sqlalchemy.Column(types.Json)
data = sqlalchemy.Column(types.Json)
tags = sqlalchemy.Column(types.Dict)
data = sqlalchemy.Column(types.Dict)
class ClusterLock(BASE, SenlinBase):
@ -175,8 +175,8 @@ class Policy(BASE, SenlinBase, SoftDelete):
cooldown = sqlalchemy.Column(sqlalchemy.Integer)
level = sqlalchemy.Column(sqlalchemy.Integer)
deleted_time = sqlalchemy.Column(sqlalchemy.DateTime)
spec = sqlalchemy.Column(types.Json)
data = sqlalchemy.Column(types.Json)
spec = sqlalchemy.Column(types.Dict)
data = sqlalchemy.Column(types.Dict)
class ClusterPolicies(BASE, SenlinBase):
@ -207,9 +207,9 @@ class Profile(BASE, SenlinBase, SoftDelete):
default=lambda: str(uuid.uuid4()))
name = sqlalchemy.Column(sqlalchemy.String(255))
type = sqlalchemy.Column(sqlalchemy.String(255))
spec = sqlalchemy.Column(types.Json)
spec = sqlalchemy.Column(types.Dict)
permission = sqlalchemy.Column(sqlalchemy.String(32))
tags = sqlalchemy.Column(types.Json)
tags = sqlalchemy.Column(types.Dict)
created_time = sqlalchemy.Column(sqlalchemy.DateTime)
updated_time = sqlalchemy.Column(sqlalchemy.DateTime)
deleted_time = sqlalchemy.Column(sqlalchemy.DateTime)
@ -223,9 +223,9 @@ class Action(BASE, SenlinBase, SoftDelete):
id = sqlalchemy.Column('id', sqlalchemy.String(36), primary_key=True,
default=lambda: str(uuid.uuid4()))
name = sqlalchemy.Column(sqlalchemy.String(63))
context = sqlalchemy.Column(types.Json)
context = sqlalchemy.Column(types.Dict)
target = sqlalchemy.Column(sqlalchemy.String(36))
action = sqlalchemy.Column(types.LongText)
action = sqlalchemy.Column(sqlalchemy.Text)
cause = sqlalchemy.Column(sqlalchemy.String(255))
owner = sqlalchemy.Column(sqlalchemy.String(36))
interval = sqlalchemy.Column(sqlalchemy.Integer)
@ -235,10 +235,10 @@ class Action(BASE, SenlinBase, SoftDelete):
status = sqlalchemy.Column(sqlalchemy.String(255))
status_reason = sqlalchemy.Column(sqlalchemy.String(255))
control = sqlalchemy.Column(sqlalchemy.String(255))
inputs = sqlalchemy.Column(types.Json)
outputs = sqlalchemy.Column(types.Json)
depends_on = sqlalchemy.Column(types.Json)
depended_by = sqlalchemy.Column(types.Json)
inputs = sqlalchemy.Column(types.Dict)
outputs = sqlalchemy.Column(types.Dict)
depends_on = sqlalchemy.Column(types.List)
depended_by = sqlalchemy.Column(types.List)
deleted_time = sqlalchemy.Column(sqlalchemy.DateTime)

View File

@ -1,27 +1,51 @@
#
# 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
# 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.
# 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 json
from sqlalchemy.dialects import mysql
from sqlalchemy.ext import mutable
from sqlalchemy import types
dumps = json.dumps
loads = json.loads
class MutableList(mutable.Mutable, list):
@classmethod
def coerce(cls, key, value):
if not isinstance(value, cls):
if isinstance(value, list):
return cls(value)
return mutable.Mutable.coerce(key, value)
else:
return value
def __delitem__(self, key):
list.__delitem__(self, key)
self.changed()
def __setitem__(self, key, value):
list.__setitem__(self, key, value)
self.changed()
def __getstate__(self):
return list(self)
def __setstate__(self, state):
len = list.__len__(self)
list.__delslice__(self, 0, len)
list.__add__(self, state)
self.changed()
class LongText(types.TypeDecorator):
class Dict(types.TypeDecorator):
impl = types.Text
def load_dialect_impl(self, dialect):
@ -30,27 +54,32 @@ class LongText(types.TypeDecorator):
else:
return self.impl
class Json(LongText):
def process_bind_param(self, value, dialect):
return dumps(value)
return json.dumps(value)
def process_result_value(self, value, dialect):
if value is None:
return None
return loads(value)
return json.loads(value)
def associate_with(sqltype):
# TODO(leizhang) When we removed sqlalchemy 0.7 dependence
# we can import MutableDict directly and remove ./mutable.py
try:
from sqlalchemy.ext import mutable
mutable.MutableDict.associate_with(Json)
except ImportError:
from senlin.db.sqlalchemy import mutable
mutable.MutableDict.associate_with(Json)
class List(types.TypeDecorator):
impl = types.Text
associate_with(LongText)
associate_with(Json)
def load_dialect_impl(self, dialect):
if dialect.name == 'mysql':
return dialect.type_descriptor(mysql.LONGTEXT())
else:
return self.impl
def process_bind_param(self, value, dialect):
return json.dumps(value)
def process_result_value(self, value, dialect):
if value is None:
return None
return json.loads(value)
mutable.MutableDict.associate_with(Dict)
MutableList.associate_with(List)