0491665298279fa3be0be47bebe96430b108a600
[openstack-build/neutron-build.git] / neutron / db / flavors_db.py
1 # All Rights Reserved.
2 #
3 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
4 #    not use this file except in compliance with the License. You may obtain
5 #    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, WITHOUT
11 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 #    License for the specific language governing permissions and limitations
13 #    under the License.
14
15 from oslo_log import log as logging
16 from oslo_utils import uuidutils
17 import sqlalchemy as sa
18 from sqlalchemy import orm
19 from sqlalchemy.orm import exc as sa_exc
20
21 from neutron.api.v2 import attributes as attr
22 from neutron.db import common_db_mixin
23 from neutron.db import model_base
24 from neutron.db import servicetype_db as sdb
25 from neutron.extensions import flavors as ext_flavors
26
27 LOG = logging.getLogger(__name__)
28
29
30 class Flavor(model_base.BASEV2, model_base.HasId):
31     name = sa.Column(sa.String(attr.NAME_MAX_LEN))
32     description = sa.Column(sa.String(attr.LONG_DESCRIPTION_MAX_LEN))
33     enabled = sa.Column(sa.Boolean, nullable=False, default=True,
34                         server_default=sa.sql.true())
35     # Make it True for multi-type flavors
36     service_type = sa.Column(sa.String(36), nullable=True)
37     service_profiles = orm.relationship("FlavorServiceProfileBinding",
38         cascade="all, delete-orphan")
39
40
41 class ServiceProfile(model_base.BASEV2, model_base.HasId):
42     description = sa.Column(sa.String(attr.LONG_DESCRIPTION_MAX_LEN))
43     driver = sa.Column(sa.String(1024), nullable=False)
44     enabled = sa.Column(sa.Boolean, nullable=False, default=True,
45                         server_default=sa.sql.true())
46     metainfo = sa.Column(sa.String(4096))
47     flavors = orm.relationship("FlavorServiceProfileBinding")
48
49
50 class FlavorServiceProfileBinding(model_base.BASEV2):
51     flavor_id = sa.Column(sa.String(36),
52                           sa.ForeignKey("flavors.id",
53                                         ondelete="CASCADE"),
54                           nullable=False, primary_key=True)
55     flavor = orm.relationship(Flavor)
56     service_profile_id = sa.Column(sa.String(36),
57                                    sa.ForeignKey("serviceprofiles.id",
58                                                  ondelete="CASCADE"),
59                                    nullable=False, primary_key=True)
60     service_profile = orm.relationship(ServiceProfile)
61
62
63 class FlavorsDbMixin(common_db_mixin.CommonDbMixin):
64
65     """Class to support flavors and service profiles."""
66
67     def _get_flavor(self, context, flavor_id):
68         try:
69             return self._get_by_id(context, Flavor, flavor_id)
70         except sa_exc.NoResultFound:
71             raise ext_flavors.FlavorNotFound(flavor_id=flavor_id)
72
73     def _get_service_profile(self, context, sp_id):
74         try:
75             return self._get_by_id(context, ServiceProfile, sp_id)
76         except sa_exc.NoResultFound:
77             raise ext_flavors.ServiceProfileNotFound(sp_id=sp_id)
78
79     def _make_flavor_dict(self, flavor_db, fields=None):
80         res = {'id': flavor_db['id'],
81                'name': flavor_db['name'],
82                'description': flavor_db['description'],
83                'service_type': flavor_db['service_type'],
84                'enabled': flavor_db['enabled'],
85                'service_profiles': []}
86         if flavor_db.service_profiles:
87             res['service_profiles'] = [sp['service_profile_id']
88                                        for sp in flavor_db.service_profiles]
89         return self._fields(res, fields)
90
91     def _make_service_profile_dict(self, sp_db, fields=None):
92         res = {'id': sp_db['id'],
93                'description': sp_db['description'],
94                'driver': sp_db['driver'],
95                'enabled': sp_db['enabled'],
96                'metainfo': sp_db['metainfo']}
97         if sp_db.flavors:
98             res['flavors'] = [fl['flavor_id']
99                               for fl in sp_db.flavors]
100         return self._fields(res, fields)
101
102     def _ensure_flavor_not_in_use(self, context, flavor_id):
103         """Checks that flavor is not associated with service instance."""
104         # Future TODO(enikanorov): check that there is no binding to
105         # instances. Shall address in future upon getting the right
106         # flavor supported driver
107         pass
108
109     def _ensure_service_profile_not_in_use(self, context, sp_id):
110         """Ensures no current bindings to flavors exist."""
111         fl = (context.session.query(FlavorServiceProfileBinding).
112               filter_by(service_profile_id=sp_id).first())
113         if fl:
114             raise ext_flavors.ServiceProfileInUse(sp_id=sp_id)
115
116     def _validate_driver(self, context, driver):
117         """Confirms a non-empty driver is a valid provider."""
118         service_type_manager = sdb.ServiceTypeManager.get_instance()
119         providers = service_type_manager.get_service_providers(
120             context,
121             filters={'driver': driver})
122
123         if not providers:
124             raise ext_flavors.ServiceProfileDriverNotFound(driver=driver)
125
126     def create_flavor(self, context, flavor):
127         fl = flavor['flavor']
128         with context.session.begin(subtransactions=True):
129             fl_db = Flavor(id=uuidutils.generate_uuid(),
130                            name=fl['name'],
131                            description=fl['description'],
132                            service_type=fl['service_type'],
133                            enabled=fl['enabled'])
134             context.session.add(fl_db)
135         return self._make_flavor_dict(fl_db)
136
137     def update_flavor(self, context, flavor_id, flavor):
138         fl = flavor['flavor']
139         with context.session.begin(subtransactions=True):
140             self._ensure_flavor_not_in_use(context, flavor_id)
141             fl_db = self._get_flavor(context, flavor_id)
142             fl_db.update(fl)
143         return self._make_flavor_dict(fl_db)
144
145     def get_flavor(self, context, flavor_id, fields=None):
146         fl = self._get_flavor(context, flavor_id)
147         return self._make_flavor_dict(fl, fields)
148
149     def delete_flavor(self, context, flavor_id):
150         with context.session.begin(subtransactions=True):
151             self._ensure_flavor_not_in_use(context, flavor_id)
152             fl_db = self._get_flavor(context, flavor_id)
153             context.session.delete(fl_db)
154
155     def get_flavors(self, context, filters=None, fields=None,
156                     sorts=None, limit=None, marker=None, page_reverse=False):
157         return self._get_collection(context, Flavor, self._make_flavor_dict,
158                                     filters=filters, fields=fields,
159                                     sorts=sorts, limit=limit,
160                                     marker_obj=marker,
161                                     page_reverse=page_reverse)
162
163     def create_flavor_service_profile(self, context,
164                                       service_profile, flavor_id):
165         sp = service_profile['service_profile']
166         with context.session.begin(subtransactions=True):
167             bind_qry = context.session.query(FlavorServiceProfileBinding)
168             binding = bind_qry.filter_by(service_profile_id=sp['id'],
169                                          flavor_id=flavor_id).first()
170             if binding:
171                 raise ext_flavors.FlavorServiceProfileBindingExists(
172                     sp_id=sp['id'], fl_id=flavor_id)
173             binding = FlavorServiceProfileBinding(
174                 service_profile_id=sp['id'],
175                 flavor_id=flavor_id)
176             context.session.add(binding)
177         fl_db = self._get_flavor(context, flavor_id)
178         return self._make_flavor_dict(fl_db)
179
180     def delete_flavor_service_profile(self, context,
181                                       service_profile_id, flavor_id):
182         with context.session.begin(subtransactions=True):
183             binding = (context.session.query(FlavorServiceProfileBinding).
184                        filter_by(service_profile_id=service_profile_id,
185                        flavor_id=flavor_id).first())
186             if not binding:
187                 raise ext_flavors.FlavorServiceProfileBindingNotFound(
188                     sp_id=service_profile_id, fl_id=flavor_id)
189             context.session.delete(binding)
190
191     def get_flavor_service_profile(self, context,
192                                    service_profile_id, flavor_id, fields=None):
193         with context.session.begin(subtransactions=True):
194             binding = (context.session.query(FlavorServiceProfileBinding).
195                        filter_by(service_profile_id=service_profile_id,
196                        flavor_id=flavor_id).first())
197             if not binding:
198                 raise ext_flavors.FlavorServiceProfileBindingNotFound(
199                     sp_id=service_profile_id, fl_id=flavor_id)
200         res = {'service_profile_id': service_profile_id,
201                'flavor_id': flavor_id}
202         return self._fields(res, fields)
203
204     def create_service_profile(self, context, service_profile):
205         sp = service_profile['service_profile']
206
207         if sp['driver']:
208             self._validate_driver(context, sp['driver'])
209         else:
210             if not sp['metainfo']:
211                 raise ext_flavors.ServiceProfileEmpty()
212
213         with context.session.begin(subtransactions=True):
214             sp_db = ServiceProfile(id=uuidutils.generate_uuid(),
215                                    description=sp['description'],
216                                    driver=sp['driver'],
217                                    enabled=sp['enabled'],
218                                    metainfo=sp['metainfo'])
219             context.session.add(sp_db)
220
221         return self._make_service_profile_dict(sp_db)
222
223     def update_service_profile(self, context,
224                                service_profile_id, service_profile):
225         sp = service_profile['service_profile']
226
227         if sp.get('driver'):
228             self._validate_driver(context, sp['driver'])
229
230         with context.session.begin(subtransactions=True):
231             self._ensure_service_profile_not_in_use(context,
232                                                     service_profile_id)
233             sp_db = self._get_service_profile(context, service_profile_id)
234             sp_db.update(sp)
235         return self._make_service_profile_dict(sp_db)
236
237     def get_service_profile(self, context, sp_id, fields=None):
238         sp_db = self._get_service_profile(context, sp_id)
239         return self._make_service_profile_dict(sp_db, fields)
240
241     def delete_service_profile(self, context, sp_id):
242         with context.session.begin(subtransactions=True):
243             self._ensure_service_profile_not_in_use(context, sp_id)
244             sp_db = self._get_service_profile(context, sp_id)
245             context.session.delete(sp_db)
246
247     def get_service_profiles(self, context, filters=None, fields=None,
248                              sorts=None, limit=None, marker=None,
249                              page_reverse=False):
250         return self._get_collection(context, ServiceProfile,
251                                     self._make_service_profile_dict,
252                                     filters=filters, fields=fields,
253                                     sorts=sorts, limit=limit,
254                                     marker_obj=marker,
255                                     page_reverse=page_reverse)
256
257     def get_flavor_next_provider(self, context, flavor_id,
258                                  filters=None, fields=None,
259                                  sorts=None, limit=None,
260                                  marker=None, page_reverse=False):
261         """From flavor, choose service profile and find provider for driver."""
262
263         with context.session.begin(subtransactions=True):
264             bind_qry = context.session.query(FlavorServiceProfileBinding)
265             binding = bind_qry.filter_by(flavor_id=flavor_id).first()
266             if not binding:
267                 raise ext_flavors.FlavorServiceProfileBindingNotFound(
268                     sp_id='', fl_id=flavor_id)
269
270         # Get the service profile from the first binding
271         # TODO(jwarendt) Should become a scheduling framework instead
272         sp_db = self._get_service_profile(context,
273                                           binding['service_profile_id'])
274
275         if not sp_db.enabled:
276             raise ext_flavors.ServiceProfileDisabled()
277
278         LOG.debug("Found driver %s.", sp_db.driver)
279
280         service_type_manager = sdb.ServiceTypeManager.get_instance()
281         providers = service_type_manager.get_service_providers(
282             context,
283             filters={'driver': sp_db.driver})
284
285         if not providers:
286             raise ext_flavors.ServiceProfileDriverNotFound(driver=sp_db.driver)
287
288         LOG.debug("Found providers %s.", providers)
289
290         res = {'driver': sp_db.driver,
291                'provider': providers[0].get('name')}
292
293         return [self._fields(res, fields)]