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
7 # http://www.apache.org/licenses/LICENSE-2.0
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
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
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
27 LOG = logging.getLogger(__name__)
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")
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")
50 class FlavorServiceProfileBinding(model_base.BASEV2):
51 flavor_id = sa.Column(sa.String(36),
52 sa.ForeignKey("flavors.id",
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",
59 nullable=False, primary_key=True)
60 service_profile = orm.relationship(ServiceProfile)
63 class FlavorsDbMixin(common_db_mixin.CommonDbMixin):
65 """Class to support flavors and service profiles."""
67 def _get_flavor(self, context, flavor_id):
69 return self._get_by_id(context, Flavor, flavor_id)
70 except sa_exc.NoResultFound:
71 raise ext_flavors.FlavorNotFound(flavor_id=flavor_id)
73 def _get_service_profile(self, context, sp_id):
75 return self._get_by_id(context, ServiceProfile, sp_id)
76 except sa_exc.NoResultFound:
77 raise ext_flavors.ServiceProfileNotFound(sp_id=sp_id)
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)
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']}
98 res['flavors'] = [fl['flavor_id']
99 for fl in sp_db.flavors]
100 return self._fields(res, fields)
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
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())
114 raise ext_flavors.ServiceProfileInUse(sp_id=sp_id)
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(
121 filters={'driver': driver})
124 raise ext_flavors.ServiceProfileDriverNotFound(driver=driver)
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(),
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)
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)
143 return self._make_flavor_dict(fl_db)
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)
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)
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,
161 page_reverse=page_reverse)
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()
171 raise ext_flavors.FlavorServiceProfileBindingExists(
172 sp_id=sp['id'], fl_id=flavor_id)
173 binding = FlavorServiceProfileBinding(
174 service_profile_id=sp['id'],
176 context.session.add(binding)
177 fl_db = self._get_flavor(context, flavor_id)
178 return self._make_flavor_dict(fl_db)
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())
187 raise ext_flavors.FlavorServiceProfileBindingNotFound(
188 sp_id=service_profile_id, fl_id=flavor_id)
189 context.session.delete(binding)
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())
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)
204 def create_service_profile(self, context, service_profile):
205 sp = service_profile['service_profile']
208 self._validate_driver(context, sp['driver'])
210 if not sp['metainfo']:
211 raise ext_flavors.ServiceProfileEmpty()
213 with context.session.begin(subtransactions=True):
214 sp_db = ServiceProfile(id=uuidutils.generate_uuid(),
215 description=sp['description'],
217 enabled=sp['enabled'],
218 metainfo=sp['metainfo'])
219 context.session.add(sp_db)
221 return self._make_service_profile_dict(sp_db)
223 def update_service_profile(self, context,
224 service_profile_id, service_profile):
225 sp = service_profile['service_profile']
228 self._validate_driver(context, sp['driver'])
230 with context.session.begin(subtransactions=True):
231 self._ensure_service_profile_not_in_use(context,
233 sp_db = self._get_service_profile(context, service_profile_id)
235 return self._make_service_profile_dict(sp_db)
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)
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)
247 def get_service_profiles(self, context, filters=None, fields=None,
248 sorts=None, limit=None, marker=None,
250 return self._get_collection(context, ServiceProfile,
251 self._make_service_profile_dict,
252 filters=filters, fields=fields,
253 sorts=sorts, limit=limit,
255 page_reverse=page_reverse)
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."""
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()
267 raise ext_flavors.FlavorServiceProfileBindingNotFound(
268 sp_id='', fl_id=flavor_id)
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'])
275 if not sp_db.enabled:
276 raise ext_flavors.ServiceProfileDisabled()
278 LOG.debug("Found driver %s.", sp_db.driver)
280 service_type_manager = sdb.ServiceTypeManager.get_instance()
281 providers = service_type_manager.get_service_providers(
283 filters={'driver': sp_db.driver})
286 raise ext_flavors.ServiceProfileDriverNotFound(driver=sp_db.driver)
288 LOG.debug("Found providers %s.", providers)
290 res = {'driver': sp_db.driver,
291 'provider': providers[0].get('name')}
293 return [self._fields(res, fields)]