cloudkitty/cloudkitty/rating/hash/db/sqlalchemy/api.py

487 lines
18 KiB
Python

# -*- coding: utf-8 -*-
# Copyright 2014 Objectif Libre
#
# 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.
#
# @author: Stéphane Albert
#
from oslo_db import exception
from oslo_db.sqlalchemy import utils
from oslo_log import log as logging
from oslo_utils import uuidutils
import six
import sqlalchemy
from cloudkitty import db
from cloudkitty.rating.hash.db import api
from cloudkitty.rating.hash.db.sqlalchemy import migration
from cloudkitty.rating.hash.db.sqlalchemy import models
LOG = logging.getLogger(__name__)
def get_backend():
return HashMap()
class HashMap(api.HashMap):
def get_migration(self):
return migration
def get_service(self, name=None, uuid=None):
session = db.get_session()
try:
q = session.query(models.HashMapService)
if name:
q = q.filter(
models.HashMapService.name == name)
elif uuid:
q = q.filter(
models.HashMapService.service_id == uuid)
else:
raise ValueError('You must specify either name or uuid.')
res = q.one()
return res
except sqlalchemy.orm.exc.NoResultFound:
raise api.NoSuchService(name=name, uuid=uuid)
def get_field(self, uuid=None, service_uuid=None, name=None):
session = db.get_session()
try:
q = session.query(models.HashMapField)
if uuid:
q = q.filter(
models.HashMapField.field_id == uuid)
elif service_uuid and name:
q = q.join(
models.HashMapField.service)
q = q.filter(
models.HashMapService.service_id == service_uuid,
models.HashMapField.name == name)
else:
raise ValueError('You must specify either an uuid'
' or a service_uuid and a name.')
res = q.one()
return res
except sqlalchemy.orm.exc.NoResultFound:
raise api.NoSuchField(uuid)
def get_group(self, uuid):
session = db.get_session()
try:
q = session.query(models.HashMapGroup)
q = q.filter(
models.HashMapGroup.group_id == uuid)
res = q.one()
return res
except sqlalchemy.orm.exc.NoResultFound:
raise api.NoSuchGroup(uuid=uuid)
def get_mapping(self, uuid):
session = db.get_session()
try:
q = session.query(models.HashMapMapping)
q = q.filter(
models.HashMapMapping.mapping_id == uuid)
res = q.one()
return res
except sqlalchemy.orm.exc.NoResultFound:
raise api.NoSuchMapping(uuid)
def get_threshold(self, uuid):
session = db.get_session()
try:
q = session.query(models.HashMapThreshold)
q = q.filter(
models.HashMapThreshold.threshold_id == uuid)
res = q.one()
return res
except sqlalchemy.orm.exc.NoResultFound:
raise api.NoSuchThreshold(uuid)
def get_group_from_mapping(self, uuid):
session = db.get_session()
try:
q = session.query(models.HashMapGroup)
q = q.join(
models.HashMapGroup.mappings)
q = q.filter(
models.HashMapMapping.mapping_id == uuid)
res = q.one()
return res
except sqlalchemy.orm.exc.NoResultFound:
raise api.MappingHasNoGroup(uuid=uuid)
def get_group_from_threshold(self, uuid):
session = db.get_session()
try:
q = session.query(models.HashMapGroup)
q = q.join(
models.HashMapGroup.thresholds)
q = q.filter(
models.HashMapThreshold.threshold_id == uuid)
res = q.one()
return res
except sqlalchemy.orm.exc.NoResultFound:
raise api.ThresholdHasNoGroup(uuid=uuid)
def list_services(self):
session = db.get_session()
q = session.query(models.HashMapService)
res = q.values(
models.HashMapService.service_id)
return [uuid[0] for uuid in res]
def list_fields(self, service_uuid):
session = db.get_session()
q = session.query(models.HashMapField)
q = q.join(
models.HashMapField.service)
q = q.filter(
models.HashMapService.service_id == service_uuid)
res = q.values(models.HashMapField.field_id)
return [uuid[0] for uuid in res]
def list_groups(self):
session = db.get_session()
q = session.query(models.HashMapGroup)
res = q.values(
models.HashMapGroup.group_id)
return [uuid[0] for uuid in res]
def list_mappings(self,
service_uuid=None,
field_uuid=None,
group_uuid=None,
no_group=False):
session = db.get_session()
q = session.query(models.HashMapMapping)
if service_uuid:
q = q.join(
models.HashMapMapping.service)
q = q.filter(
models.HashMapService.service_id == service_uuid)
elif field_uuid:
q = q.join(
models.HashMapMapping.field)
q = q.filter(models.HashMapField.field_id == field_uuid)
if group_uuid:
q = q.join(
models.HashMapMapping.group)
q = q.filter(models.HashMapGroup.group_id == group_uuid)
elif not service_uuid and not field_uuid:
raise ValueError('You must specify either service_uuid,'
' field_uuid or group_uuid.')
elif no_group:
q = q.filter(models.HashMapMapping.group_id == None) # noqa
res = q.values(
models.HashMapMapping.mapping_id)
return [uuid[0] for uuid in res]
def list_thresholds(self,
service_uuid=None,
field_uuid=None,
group_uuid=None,
no_group=False):
session = db.get_session()
q = session.query(models.HashMapThreshold)
if service_uuid:
q = q.join(
models.HashMapThreshold.service)
q = q.filter(
models.HashMapService.service_id == service_uuid)
elif field_uuid:
q = q.join(
models.HashMapThreshold.field)
q = q.filter(models.HashMapField.field_id == field_uuid)
if group_uuid:
q = q.join(
models.HashMapThreshold.group)
q = q.filter(models.HashMapGroup.group_id == group_uuid)
elif not service_uuid and not field_uuid:
raise ValueError('You must specify either service_uuid,'
' field_uuid or group_uuid.')
elif no_group:
q = q.filter(models.HashMapThreshold.group_id == None) # noqa
res = q.values(
models.HashMapThreshold.threshold_id)
return [uuid[0] for uuid in res]
def create_service(self, name):
session = db.get_session()
try:
with session.begin():
service_db = models.HashMapService(name=name)
service_db.service_id = uuidutils.generate_uuid()
session.add(service_db)
return service_db
except exception.DBDuplicateEntry:
service_db = self.get_service(name=name)
raise api.ServiceAlreadyExists(
service_db.name,
service_db.service_id)
def create_field(self, service_uuid, name):
service_db = self.get_service(uuid=service_uuid)
session = db.get_session()
try:
with session.begin():
field_db = models.HashMapField(
service_id=service_db.id,
name=name,
field_id=uuidutils.generate_uuid())
session.add(field_db)
# FIXME(sheeprine): backref are not populated as they used to be.
# Querying the item again to get backref.
field_db = self.get_field(service_uuid=service_uuid,
name=name)
except exception.DBDuplicateEntry:
raise api.FieldAlreadyExists(field_db.name, field_db.field_id)
else:
return field_db
def create_group(self, name):
session = db.get_session()
try:
with session.begin():
group_db = models.HashMapGroup(
name=name,
group_id=uuidutils.generate_uuid())
session.add(group_db)
return group_db
except exception.DBDuplicateEntry:
raise api.GroupAlreadyExists(name, group_db.group_id)
def create_mapping(self,
cost,
map_type='rate',
value=None,
service_id=None,
field_id=None,
group_id=None):
if field_id and service_id:
raise ValueError('You can only specify one parent.')
if not value and not service_id:
raise ValueError('You must either specify a value'
' or a service_id')
elif value and service_id:
raise ValueError('You can\'t specify a value'
' and a service_id.')
field_fk = None
if field_id:
field_db = self.get_field(uuid=field_id)
field_fk = field_db.id
service_fk = None
if service_id:
service_db = self.get_service(uuid=service_id)
service_fk = service_db.id
group_fk = None
if group_id:
group_db = self.get_group(uuid=group_id)
group_fk = group_db.id
session = db.get_session()
try:
with session.begin():
field_map = models.HashMapMapping(
mapping_id=uuidutils.generate_uuid(),
value=value,
cost=cost,
field_id=field_fk,
service_id=service_fk,
map_type=map_type)
if group_fk:
field_map.group_id = group_fk
session.add(field_map)
except exception.DBDuplicateEntry:
raise api.MappingAlreadyExists(value, field_map.field_id)
except exception.DBError:
raise api.NoSuchType(map_type)
# FIXME(sheeprine): backref are not populated as they used to be.
# Querying the item again to get backref.
field_map = self.get_mapping(field_map.mapping_id)
return field_map
def create_threshold(self,
level,
cost,
map_type='rate',
service_id=None,
field_id=None,
group_id=None):
if field_id and service_id:
raise ValueError('You can only specify one parent.')
field_fk = None
if field_id:
field_db = self.get_field(uuid=field_id)
field_fk = field_db.id
service_fk = None
if service_id:
service_db = self.get_service(uuid=service_id)
service_fk = service_db.id
group_fk = None
if group_id:
group_db = self.get_group(uuid=group_id)
group_fk = group_db.id
session = db.get_session()
try:
with session.begin():
threshold_db = models.HashMapThreshold(
threshold_id=uuidutils.generate_uuid(),
level=level,
cost=cost,
field_id=field_fk,
service_id=service_fk,
map_type=map_type)
if group_fk:
threshold_db.group_id = group_fk
session.add(threshold_db)
except exception.DBDuplicateEntry:
raise api.ThresholdAlreadyExists(level, threshold_db.field_id)
except exception.DBError:
raise api.NoSuchType(map_type)
# FIXME(sheeprine): backref are not populated as they used to be.
# Querying the item again to get backref.
threshold_db = self.get_threshold(threshold_db.threshold_id)
return threshold_db
def update_mapping(self, uuid, **kwargs):
session = db.get_session()
try:
with session.begin():
q = session.query(models.HashMapMapping)
q = q.filter(
models.HashMapMapping.mapping_id == uuid
)
mapping_db = q.with_lockmode('update').one()
if kwargs:
# Resolve FK
if 'group_id' in kwargs:
group_id = kwargs.pop('group_id')
if group_id:
group_db = self.get_group(group_id)
mapping_db.group_id = group_db.id
# Service and Field shouldn't be updated
excluded_cols = ['mapping_id', 'service_id', 'field_id']
for col in excluded_cols:
if col in kwargs:
kwargs.pop(col)
for attribute, value in six.iteritems(kwargs):
if hasattr(mapping_db, attribute):
setattr(mapping_db, attribute, value)
else:
raise ValueError('No such attribute: {}'.format(
attribute))
else:
raise ValueError('No attribute to update.')
return mapping_db
except sqlalchemy.orm.exc.NoResultFound:
raise api.NoSuchMapping(uuid)
def update_threshold(self, uuid, **kwargs):
session = db.get_session()
try:
with session.begin():
q = session.query(models.HashMapThreshold)
q = q.filter(
models.HashMapThreshold.threshold_id == uuid)
threshold_db = q.with_lockmode('update').one()
if kwargs:
# Resolve FK
if 'group_id' in kwargs:
group_id = kwargs.pop('group_id')
if group_id:
group_db = self.get_group(group_id)
threshold_db.group_id = group_db.id
# Service and Field shouldn't be updated
excluded_cols = ['threshold_id', 'service_id', 'field_id']
for col in excluded_cols:
if col in kwargs:
kwargs.pop(col)
for attribute, value in six.iteritems(kwargs):
if hasattr(threshold_db, attribute):
setattr(threshold_db, attribute, value)
else:
raise ValueError('No such attribute: {}'.format(
attribute))
else:
raise ValueError('No attribute to update.')
return threshold_db
except sqlalchemy.orm.exc.NoResultFound:
raise api.NoSuchThreshold(uuid)
def delete_service(self, name=None, uuid=None):
session = db.get_session()
q = utils.model_query(
models.HashMapService,
session)
if name:
q = q.filter(models.HashMapService.name == name)
elif uuid:
q = q.filter(models.HashMapService.service_id == uuid)
else:
raise ValueError('You must specify either name or uuid.')
r = q.delete()
if not r:
raise api.NoSuchService(name, uuid)
def delete_field(self, uuid):
session = db.get_session()
q = utils.model_query(
models.HashMapField,
session)
q = q.filter(models.HashMapField.field_id == uuid)
r = q.delete()
if not r:
raise api.NoSuchField(uuid)
def delete_group(self, uuid, recurse=True):
session = db.get_session()
q = utils.model_query(
models.HashMapGroup,
session)
q = q.filter(models.HashMapGroup.group_id == uuid)
with session.begin():
try:
r = q.with_lockmode('update').one()
except sqlalchemy.orm.exc.NoResultFound:
raise api.NoSuchGroup(uuid=uuid)
if recurse:
for mapping in r.mappings:
session.delete(mapping)
for threshold in r.thresholds:
session.delete(threshold)
q.delete()
def delete_mapping(self, uuid):
session = db.get_session()
q = utils.model_query(
models.HashMapMapping,
session)
q = q.filter(models.HashMapMapping.mapping_id == uuid)
r = q.delete()
if not r:
raise api.NoSuchMapping(uuid)
def delete_threshold(self, uuid):
session = db.get_session()
q = utils.model_query(
models.HashMapThreshold,
session)
q = q.filter(models.HashMapThreshold.threshold_id == uuid)
r = q.delete()
if not r:
raise api.NoSuchThreshold(uuid)