c5a4f04576bbd661afe7915b8a15cda82b9fce5c
[openstack-build/neutron-build.git] / neutron / db / model_base.py
1 # Copyright (c) 2012 OpenStack Foundation.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #    http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12 # implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15
16 from oslo_db.sqlalchemy import models
17 from oslo_utils import uuidutils
18 import sqlalchemy as sa
19 from sqlalchemy.ext import declarative
20 from sqlalchemy import orm
21
22 from neutron.api.v2 import attributes as attr
23
24
25 class HasTenant(object):
26     """Tenant mixin, add to subclasses that have a tenant."""
27
28     # NOTE(jkoelker) tenant_id is just a free form string ;(
29     tenant_id = sa.Column(sa.String(attr.TENANT_ID_MAX_LEN), index=True)
30
31
32 class HasId(object):
33     """id mixin, add to subclasses that have an id."""
34
35     id = sa.Column(sa.String(36),
36                    primary_key=True,
37                    default=uuidutils.generate_uuid)
38
39
40 class HasStatusDescription(object):
41     """Status with description mixin."""
42
43     status = sa.Column(sa.String(16), nullable=False)
44     status_description = sa.Column(sa.String(attr.DESCRIPTION_MAX_LEN))
45
46
47 class NeutronBase(models.ModelBase):
48     """Base class for Neutron Models."""
49
50     __table_args__ = {'mysql_engine': 'InnoDB'}
51
52     def __iter__(self):
53         self._i = iter(orm.object_mapper(self).columns)
54         return self
55
56     def next(self):
57         n = next(self._i).name
58         return n, getattr(self, n)
59
60     __next__ = next
61
62     def __repr__(self):
63         """sqlalchemy based automatic __repr__ method."""
64         items = ['%s=%r' % (col.name, getattr(self, col.name))
65                  for col in self.__table__.columns]
66         return "<%s.%s[object at %x] {%s}>" % (self.__class__.__module__,
67                                                self.__class__.__name__,
68                                                id(self), ', '.join(items))
69
70
71 class NeutronBaseV2(NeutronBase):
72
73     @declarative.declared_attr
74     def __tablename__(cls):
75         # NOTE(jkoelker) use the pluralized name of the class as the table
76         return cls.__name__.lower() + 's'
77
78
79 BASEV2 = declarative.declarative_base(cls=NeutronBaseV2)
80
81
82 class StandardAttribute(BASEV2):
83     """Common table to associate all Neutron API resources.
84
85     By having Neutron objects related to this table, we can associate new
86     tables that apply to many Neutron objects (e.g. timestamps, rbac entries)
87     to this table to avoid schema duplication while maintaining referential
88     integrity.
89
90     NOTE(kevinbenton): This table should not have more columns added to it
91     unless we are absolutely certain the new column will have a value for
92     every single type of Neutron resource. Otherwise this table will be filled
93     with NULL entries for combinations that don't make sense. Additionally,
94     by keeping this table small we can ensure that performance isn't adversely
95     impacted for queries on objects.
96     """
97
98     # sqlite doesn't support auto increment on big integers so we use big int
99     # for everything but sqlite
100     id = sa.Column(sa.BigInteger().with_variant(sa.Integer(), 'sqlite'),
101                    primary_key=True, autoincrement=True)
102
103     # NOTE(kevinbenton): this column is redundant information, but it allows
104     # operators/devs to look at the contents of this table and know which table
105     # the corresponding object is in.
106     # 255 was selected as a max just because it's the varchar ceiling in mysql
107     # before a 2-byte prefix is required. We shouldn't get anywhere near this
108     # limit with our table names...
109     resource_type = sa.Column(sa.String(255), nullable=False)
110
111
112 class HasStandardAttributes(object):
113     @declarative.declared_attr
114     def standard_attr_id(cls):
115         return sa.Column(
116             sa.BigInteger().with_variant(sa.Integer(), 'sqlite'),
117             sa.ForeignKey(StandardAttribute.id, ondelete="CASCADE"),
118             unique=True,
119             nullable=False
120         )
121
122     # NOTE(kevinbenton): we have to disable the following pylint check because
123     # it thinks we are overriding this method in the __init__ method.
124     #pylint: disable=method-hidden
125     @declarative.declared_attr
126     def standard_attr(cls):
127         return orm.relationship(StandardAttribute,
128                                 lazy='joined',
129                                 cascade='all, delete-orphan',
130                                 single_parent=True,
131                                 uselist=False)
132
133     def __init__(self, *args, **kwargs):
134         super(HasStandardAttributes, self).__init__(*args, **kwargs)
135         # here we automatically create the related standard attribute object
136         self.standard_attr = StandardAttribute(
137             resource_type=self.__tablename__)