Merge "Add aggregates tables to the API db."
This commit is contained in:
commit
bfc13b43e7
@ -0,0 +1,73 @@
|
||||
# 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.
|
||||
"""API Database migrations for aggregates"""
|
||||
|
||||
from migrate import UniqueConstraint
|
||||
from sqlalchemy import Column
|
||||
from sqlalchemy import DateTime
|
||||
from sqlalchemy import ForeignKey
|
||||
from sqlalchemy import Index
|
||||
from sqlalchemy import Integer
|
||||
from sqlalchemy import MetaData
|
||||
from sqlalchemy import String
|
||||
from sqlalchemy import Table
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
meta = MetaData()
|
||||
meta.bind = migrate_engine
|
||||
|
||||
aggregates = Table('aggregates', meta,
|
||||
Column('created_at', DateTime),
|
||||
Column('updated_at', DateTime),
|
||||
Column('id', Integer, primary_key=True, nullable=False),
|
||||
Column('uuid', String(length=36)),
|
||||
Column('name', String(length=255)),
|
||||
Index('aggregate_uuid_idx', 'uuid'),
|
||||
UniqueConstraint('name', name='uniq_aggregate0name'),
|
||||
mysql_engine='InnoDB',
|
||||
mysql_charset='utf8'
|
||||
)
|
||||
|
||||
aggregates.create(checkfirst=True)
|
||||
|
||||
aggregate_hosts = Table('aggregate_hosts', meta,
|
||||
Column('created_at', DateTime),
|
||||
Column('updated_at', DateTime),
|
||||
Column('id', Integer, primary_key=True, nullable=False),
|
||||
Column('host', String(length=255)),
|
||||
Column('aggregate_id', Integer, ForeignKey('aggregates.id'),
|
||||
nullable=False),
|
||||
UniqueConstraint('host', 'aggregate_id',
|
||||
name='uniq_aggregate_hosts0host0aggregate_id'),
|
||||
mysql_engine='InnoDB',
|
||||
mysql_charset='utf8'
|
||||
)
|
||||
|
||||
aggregate_hosts.create(checkfirst=True)
|
||||
|
||||
aggregate_metadata = Table('aggregate_metadata', meta,
|
||||
Column('created_at', DateTime),
|
||||
Column('updated_at', DateTime),
|
||||
Column('id', Integer, primary_key=True, nullable=False),
|
||||
Column('aggregate_id', Integer, ForeignKey('aggregates.id'),
|
||||
nullable=False),
|
||||
Column('key', String(length=255), nullable=False),
|
||||
Column('value', String(length=255), nullable=False),
|
||||
UniqueConstraint('aggregate_id', 'key',
|
||||
name='uniq_aggregate_metadata0aggregate_id0key'),
|
||||
Index('aggregate_metadata_key_idx', 'key'),
|
||||
mysql_engine='InnoDB',
|
||||
mysql_charset='utf8'
|
||||
)
|
||||
|
||||
aggregate_metadata.create(checkfirst=True)
|
@ -35,6 +35,68 @@ class _NovaAPIBase(models.ModelBase, models.TimestampMixin):
|
||||
API_BASE = declarative_base(cls=_NovaAPIBase)
|
||||
|
||||
|
||||
class AggregateHost(API_BASE):
|
||||
"""Represents a host that is member of an aggregate."""
|
||||
__tablename__ = 'aggregate_hosts'
|
||||
__table_args__ = (schema.UniqueConstraint(
|
||||
"host", "aggregate_id",
|
||||
name="uniq_aggregate_hosts0host0aggregate_id"
|
||||
),
|
||||
)
|
||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||
host = Column(String(255))
|
||||
aggregate_id = Column(Integer, ForeignKey('aggregates.id'), nullable=False)
|
||||
|
||||
|
||||
class AggregateMetadata(API_BASE):
|
||||
"""Represents a metadata key/value pair for an aggregate."""
|
||||
__tablename__ = 'aggregate_metadata'
|
||||
__table_args__ = (
|
||||
schema.UniqueConstraint("aggregate_id", "key",
|
||||
name="uniq_aggregate_metadata0aggregate_id0key"
|
||||
),
|
||||
Index('aggregate_metadata_key_idx', 'key'),
|
||||
)
|
||||
id = Column(Integer, primary_key=True)
|
||||
key = Column(String(255), nullable=False)
|
||||
value = Column(String(255), nullable=False)
|
||||
aggregate_id = Column(Integer, ForeignKey('aggregates.id'), nullable=False)
|
||||
|
||||
|
||||
class Aggregate(API_BASE):
|
||||
"""Represents a cluster of hosts that exists in this zone."""
|
||||
__tablename__ = 'aggregates'
|
||||
__table_args__ = (Index('aggregate_uuid_idx', 'uuid'),
|
||||
schema.UniqueConstraint(
|
||||
"name", name="uniq_aggregate0name")
|
||||
)
|
||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||
uuid = Column(String(36))
|
||||
name = Column(String(255))
|
||||
_hosts = orm.relationship(AggregateHost,
|
||||
primaryjoin='Aggregate.id == AggregateHost.aggregate_id')
|
||||
_metadata = orm.relationship(AggregateMetadata,
|
||||
primaryjoin='Aggregate.id == AggregateMetadata.aggregate_id')
|
||||
|
||||
@property
|
||||
def _extra_keys(self):
|
||||
return ['hosts', 'metadetails', 'availability_zone']
|
||||
|
||||
@property
|
||||
def hosts(self):
|
||||
return [h.host for h in self._hosts]
|
||||
|
||||
@property
|
||||
def metadetails(self):
|
||||
return {m.key: m.value for m in self._metadata}
|
||||
|
||||
@property
|
||||
def availability_zone(self):
|
||||
if 'availability_zone' not in self.metadetails:
|
||||
return None
|
||||
return self.metadetails['availability_zone']
|
||||
|
||||
|
||||
class CellMapping(API_BASE):
|
||||
"""Contains information on communicating with a cell"""
|
||||
__tablename__ = 'cell_mappings'
|
||||
|
57
nova/tests/functional/db/test_aggregate_model.py
Normal file
57
nova/tests/functional/db/test_aggregate_model.py
Normal file
@ -0,0 +1,57 @@
|
||||
# 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 nova.db.sqlalchemy import api_models
|
||||
from nova.db.sqlalchemy import models
|
||||
from nova import test
|
||||
|
||||
|
||||
class AggregateTablesCompareTestCase(test.NoDBTestCase):
|
||||
def _get_column_list(self, model):
|
||||
column_list = [m.key for m in model.__table__.columns]
|
||||
return column_list
|
||||
|
||||
def _check_column_list(self,
|
||||
columns_new,
|
||||
columns_old,
|
||||
added=None,
|
||||
removed=None):
|
||||
for c in added or []:
|
||||
columns_new.remove(c)
|
||||
for c in removed or []:
|
||||
columns_old.remove(c)
|
||||
intersect = set(columns_new).intersection(set(columns_old))
|
||||
if intersect != set(columns_new) or intersect != set(columns_old):
|
||||
return False
|
||||
return True
|
||||
|
||||
def _compare_models(self, m_a, m_b,
|
||||
added=None, removed=None):
|
||||
added = added or []
|
||||
removed = removed or ['deleted_at', 'deleted']
|
||||
c_a = self._get_column_list(m_a)
|
||||
c_b = self._get_column_list(m_b)
|
||||
self.assertTrue(self._check_column_list(c_a, c_b,
|
||||
added=added,
|
||||
removed=removed))
|
||||
|
||||
def test_tables_aggregate_hosts(self):
|
||||
self._compare_models(api_models.AggregateHost(),
|
||||
models.AggregateHost())
|
||||
|
||||
def test_tables_aggregate_metadata(self):
|
||||
self._compare_models(api_models.AggregateMetadata(),
|
||||
models.AggregateMetadata())
|
||||
|
||||
def test_tables_aggregates(self):
|
||||
self._compare_models(api_models.Aggregate(),
|
||||
models.Aggregate())
|
Loading…
Reference in New Issue
Block a user