Skip to content

Commit

Permalink
Add Qtree resource support to delfin
Browse files Browse the repository at this point in the history
  • Loading branch information
joseph-v committed Feb 18, 2021
1 parent 20db178 commit 84b1cd5
Show file tree
Hide file tree
Showing 12 changed files with 407 additions and 10 deletions.
55 changes: 55 additions & 0 deletions delfin/api/v1/qtrees.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Copyright 2021 The SODA Authors.
#
# 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 delfin import db
from delfin.api import api_utils
from delfin.api.common import wsgi
from delfin.api.views import qtrees as qtree_view


class QtreeController(wsgi.Controller):

def __init__(self):
super(QtreeController, self).__init__()
self.search_options = ['name', 'state', 'id', 'storage_id',
'native_filesystem_id', 'quota_id',
'native_qtree_id']

def _get_qtrees_search_options(self):
"""Return qtrees search options allowed ."""
return self.search_options

def index(self, req):
ctxt = req.environ['delfin.context']
query_params = {}
query_params.update(req.GET)
# update options other than filters
sort_keys, sort_dirs = api_utils.get_sort_params(query_params)
marker, limit, offset = api_utils.get_pagination_params(query_params)
# strip out options except supported search options
api_utils.remove_invalid_options(ctxt, query_params,
self._get_qtrees_search_options())

qtrees = db.qtree_get_all(ctxt, marker, limit, sort_keys,
sort_dirs, query_params, offset)
return qtree_view.build_qtrees(qtrees)

def show(self, req, id):
ctxt = req.environ['delfin.context']
qtree = db.qtree_get(ctxt, id)
return qtree_view.build_qtree(qtree)


def create_resource():
return wsgi.Resource(QtreeController())
9 changes: 7 additions & 2 deletions delfin/api/v1/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@
from delfin.api.v1 import alert_source
from delfin.api.v1 import alerts
from delfin.api.v1 import controllers
from delfin.api.v1 import disks
from delfin.api.v1 import filesystems
from delfin.api.v1 import performance
from delfin.api.v1 import ports
from delfin.api.v1 import disks
from delfin.api.v1 import qtrees
from delfin.api.v1 import storage_pools
from delfin.api.v1 import storages
from delfin.api.v1 import volumes
from delfin.api.v1 import performance


class APIRouter(common.APIRouter):
Expand Down Expand Up @@ -114,3 +115,7 @@ def _setup_routes(self, mapper):
self.resources['filesystems'] = filesystems.create_resource()
mapper.resource("filesystems", "filesystems",
controller=self.resources['filesystems'])

self.resources['qtrees'] = qtrees.create_resource()
mapper.resource("qtrees", "qtrees",
controller=self.resources['qtrees'])
26 changes: 26 additions & 0 deletions delfin/api/views/qtrees.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Copyright 2021 The SODA Authors.
#
# 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.
import copy


def build_qtrees(qtrees):
# Build list of qtrees
views = [build_qtree(qtree)
for qtree in qtrees]
return dict(qtrees=views)


def build_qtree(qtree):
view = copy.deepcopy(qtree)
return dict(view)
11 changes: 10 additions & 1 deletion delfin/common/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ class FilesystemStatus(object):
ALL = (NORMAL, OFFLINE, UNKNOWN)


class FilesystemSecurityMode(object):
class NASSecurityMode(object):
MIXED = 'mixed'
NATIVE = 'native'
WINDOWS = 'windows'
Expand All @@ -165,6 +165,15 @@ class FilesystemSecurityMode(object):
ALL = (MIXED, NATIVE, WINDOWS, UNIX)


class QuotaState(object):
NORMAL = 'normal'
SOFT = 'soft_limit'
HARD = 'hard_limit'
ABNORMAL = 'abnormal'

ALL = (NORMAL, SOFT, HARD, ABNORMAL)


# Enumerations for alert severity
class Severity(object):
FATAL = 'Fatal'
Expand Down
58 changes: 58 additions & 0 deletions delfin/db/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,64 @@ def filesystem_get_all(context, marker=None, limit=None, sort_keys=None,
sort_dirs, filters, offset)


def qtrees_create(context, values):
"""Create multiple qtrees."""
return IMPL.qtrees_create(context, values)


def qtrees_update(context, values):
"""Update multiple qtrees."""
return IMPL.qtrees_update(context, values)


def qtrees_delete(context, values):
"""Delete multiple qtrees."""
return IMPL.qtrees_delete(context, values)


def qtree_create(context, values):
"""Create a qtree from the values dictionary."""
return IMPL.qtree_create(context, values)


def qtree_update(context, qtree_id, values):
"""Update a qtree with the values dictionary."""
return IMPL.qtree_update(context, qtree_id, values)


def qtree_get(context, qtree_id):
"""Get a qtree or raise an exception if it does not exist."""
return IMPL.qtree_get(context, qtree_id)


def qtree_delete_by_storage(context, storage_id):
"""Delete a qtree or raise an exception if it does not exist."""
return IMPL.qtree_delete_by_storage(context, storage_id)


def qtree_get_all(context, marker=None, limit=None, sort_keys=None,
sort_dirs=None, filters=None, offset=None):
"""Retrieves all qtrees.
If no sort parameters are specified then the returned volumes are sorted
first by the 'created_at' key and then by the 'id' key in descending
order.
:param context: context of this request, it's helpful to trace the request
:param marker: the last item of the previous page, used to determine the
next page of results to return
:param limit: maximum number of items to return
:param sort_keys: list of attributes by which results should be sorted,
paired with corresponding item in sort_dirs
:param sort_dirs: list of directions in which results should be sorted,
paired with corresponding item in sort_keys, for example
'desc' for descending order
:param filters: dictionary of filters
:param offset: number of items to skip
:returns: list of controllers
"""
return IMPL.qtree_get_all(context, marker, limit, sort_keys,
sort_dirs, filters, offset)


def access_info_create(context, values):
"""Create a storage access information that used to connect
to a specific storage device.
Expand Down
144 changes: 144 additions & 0 deletions delfin/db/sqlalchemy/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -1152,6 +1152,148 @@ def _process_filesystem_info_filters(query, filters):
return query


def qtrees_create(context, qtrees):
"""Create multiple qtrees."""
session = get_session()
qtrees_refs = []
with session.begin():

for qtree in qtrees:
LOG.debug('adding new qtree for native_qtree_id {0}:'
.format(qtree.get('native_qtree_id')))
if not qtree.get('id'):
qtree['id'] = uuidutils.generate_uuid()

qtree_ref = models.Qtree()
qtree_ref.update(qtree)
qtrees_refs.append(qtree_ref)

session.add_all(qtrees_refs)

return qtrees_refs


def qtrees_update(context, qtrees):
"""Update multiple qtrees."""
session = get_session()

with session.begin():
qtree_refs = []

for qtree in qtrees:
LOG.debug('updating qtree {0}:'.format(
qtree.get('id')))
query = _qtree_get_query(context, session)
result = query.filter_by(id=qtree.get('id')
).update(qtree)

if not result:
LOG.error(exception.QtreeNotFound(qtree.get(
'id')))
else:
qtree_refs.append(result)

return qtree_refs


def qtrees_delete(context, qtrees_id_list):
"""Delete multiple qtrees."""
session = get_session()
with session.begin():
for qtree_id in qtrees_id_list:
LOG.debug('deleting qtree {0}:'.format(qtree_id))
query = _qtree_get_query(context, session)
result = query.filter_by(id=qtree_id).delete()

if not result:
LOG.error(exception.QtreeNotFound(qtree_id))
return


def _qtree_get_query(context, session=None):
return model_query(context, models.Qtree, session=session)


def _qtree_get(context, qtree_id, session=None):
result = (_qtree_get_query(context, session=session)
.filter_by(id=qtree_id)
.first())

if not result:
raise exception.QtreeNotFound(qtree_id)

return result


def qtree_create(context, values):
"""Create a qtree from the values dictionary."""
if not values.get('id'):
values['id'] = uuidutils.generate_uuid()

qtree_ref = models.Qtree()
qtree_ref.update(values)

session = get_session()
with session.begin():
session.add(qtree_ref)

return _qtree_get(context,
qtree_ref['id'],
session=session)


def qtree_update(context, qtree_id, values):
"""Update a qtree with the values dictionary."""
session = get_session()

with session.begin():
query = _qtree_get_query(context, session)
result = query.filter_by(id=qtree_id).update(values)

if not result:
raise exception.QtreeNotFound(qtree_id)

return result


def qtree_get(context, qtree_id):
"""Get a qtree or raise an exception if it does not exist."""
return _qtree_get(context, qtree_id)


def qtree_delete_by_storage(context, storage_id):
"""Delete qtree or raise an exception if it does not exist."""
_qtree_get_query(context).filter_by(storage_id=storage_id).delete()


def qtree_get_all(context, marker=None, limit=None, sort_keys=None,
sort_dirs=None, filters=None, offset=None):
"""Retrieves all qtrees."""

session = get_session()
with session.begin():
# Generate the query
query = _generate_paginate_query(context, session, models.Qtree,
marker, limit, sort_keys, sort_dirs,
filters, offset,
)
# No Qtree would match, return empty list
if query is None:
return []
return query.all()


@apply_like_filters(model=models.Qtree)
def _process_qtree_info_filters(query, filters):
"""Common filter processing for qtrees queries."""
if filters:
if not is_valid_model_filters(models.Qtree, filters):
return
query = query.filter_by(**filters)

return query


def is_orm_value(obj):
"""Check if object is an ORM field or expression."""
return isinstance(obj, (sqlalchemy.orm.attributes.InstrumentedAttribute,
Expand Down Expand Up @@ -1270,6 +1412,8 @@ def alert_source_get_all(context, marker=None, limit=None, sort_keys=None,
_disk_get),
models.Filesystem: (_filesystem_get_query,
_process_filesystem_info_filters, _filesystem_get),
models.Qtree: (_qtree_get_query,
_process_qtree_info_filters, _qtree_get),
}


Expand Down
14 changes: 14 additions & 0 deletions delfin/db/sqlalchemy/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,20 @@ class Filesystem(BASE, DelfinBase):
worm = Column(Boolean)


class Qtree(BASE, DelfinBase):
"""Represents a qtree object."""
__tablename__ = 'qtrees'
id = Column(String(36), primary_key=True)
name = Column(String(255))
storage_id = Column(String(36))
native_qtree_id = Column(String(255))
native_filesystem_id = Column(String(255))
quota_id = Column(String(255))
path = Column(String(255))
security_mode = Column(String(255))
state = Column(String(255))


class AlertSource(BASE, DelfinBase):
"""Represents an alert source configuration."""
__tablename__ = 'alert_source'
Expand Down
8 changes: 8 additions & 0 deletions delfin/drivers/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,14 @@ def list_filesystems(self, context, storage_id):
else:
return []

def list_qtrees(self, context, storage_id):
"""List all qtrees from storage system."""
driver = self.driver_manager.get_driver(context, storage_id=storage_id)
if isinstance(driver, NASDriver):
return driver.list_qtrees(context)
else:
return []

def add_trap_config(self, context, storage_id, trap_config):
"""Config the trap receiver in storage system."""
pass
Expand Down
Loading

0 comments on commit 84b1cd5

Please sign in to comment.