[Keystone-Group] CRUD APIS

Change-Id: I359b8380dd9a12cf4d58190ff6c639976d518b5d
This commit is contained in:
stewie925 2019-01-29 12:53:06 -08:00 committed by hosingh000
parent 3ea2dcb191
commit 5a9f09502a
10 changed files with 719 additions and 2 deletions

View File

@ -0,0 +1,161 @@
from pecan import rest, request, response
import oslo_db
from wsmeext.pecan import wsexpose
from orm.common.orm_common.utils import api_error_utils as err_utils
from orm.common.orm_common.utils import utils
from orm.services.customer_manager.cms_rest.controllers.v1.orm.customer.regions import RegionController
from orm.services.customer_manager.cms_rest.controllers.v1.orm.customer.users import DefaultUserController
from orm.services.customer_manager.cms_rest.logger import get_logger
from orm.services.customer_manager.cms_rest.logic.error_base import ErrorStatus
from orm.services.customer_manager.cms_rest.logic.group_logic import GroupLogic
from orm.services.customer_manager.cms_rest.model.GroupModels import Group, GroupResultWrapper, GroupSummaryResponse
from orm.services.customer_manager.cms_rest.utils import authentication
LOG = get_logger(__name__)
class GroupController(rest.RestController):
regions = RegionController()
users = DefaultUserController()
@wsexpose(Group, str, rest_content_types='json')
def get(self, group_uuid):
LOG.info("GroupController - GetGroupDetails: uuid is " + group_uuid)
authentication.authorize(request, 'groups:get_one')
try:
group_logic = GroupLogic()
result = group_logic.get_group(group_uuid)
LOG.info("GroupController - GetGroupDetails finished well: " + str(result))
except ErrorStatus as exception:
LOG.log_exception("GroupController - Failed to GetGroupDetails", exception)
raise err_utils.get_error(request.transaction_id,
message=exception.message,
status_code=exception.status_code)
except Exception as exception:
LOG.log_exception("GroupController - Failed to GetGroupDetails", exception)
raise err_utils.get_error(request.transaction_id,
status_code=500,
error_details=exception.message)
return result
@wsexpose(GroupResultWrapper, body=Group, rest_content_types='json', status_code=201)
def post(self, group):
LOG.info("GroupController - CreateGroup: " + str(group))
authentication.authorize(request, 'groups:create')
try:
uuid = None
if not group.uuid:
group.uuid = None
group_logic = GroupLogic()
try:
uuid = utils.create_or_validate_uuid(group.uuid, 'groupId')
except TypeError:
raise ErrorStatus(409.1, 'Unable to create Group ID {0}'.format(group.uuid))
try:
result = group_logic.create_group(group, uuid, request.transaction_id)
except oslo_db.exception.DBDuplicateEntry as exception:
raise ErrorStatus(409.2, 'Group field {0} already exists'.format(exception.columns))
LOG.info("GroupController - Group Created: " + str(result))
utils.audit_trail('create group', request.transaction_id,
request.headers, uuid,
event_details='')
return result
except ErrorStatus as exception:
LOG.log_exception("GroupController - Failed to CreateGroup", exception)
raise err_utils.get_error(request.transaction_id,
message=exception.message,
status_code=exception.status_code)
@wsexpose(GroupResultWrapper, str, body=Group, rest_content_types='json', status_code=200)
def put(self, group_id, group):
LOG.info("GroupController - UpdateGroup: " + str(group))
authentication.authorize(request, 'groups:update')
try:
group_logic = GroupLogic()
result = group_logic.update_group(group, group_id, request.transaction_id)
response.status = 200
LOG.info("GroupController - UpdateGroup finished well: " + str(group))
utils.audit_trail('update group', request.transaction_id,
request.headers, group_id,
event_details='')
except ErrorStatus as exception:
LOG.log_exception("Failed in UpdateGroup", exception)
raise err_utils.get_error(request.transaction_id,
message=exception.message,
status_code=exception.status_code)
except Exception as exception:
LOG.log_exception("GroupController - Failed to UpdateGroup", exception)
raise err_utils.get_error(request.transaction_id,
status_code=500,
error_details=exception.message)
return result
@wsexpose(GroupSummaryResponse, str, str, str, str, int, int,
rest_content_types='json')
def get_all(self, region=None, user=None, starts_with=None,
contains=None, start=0, limit=0):
LOG.info("GroupController - GetGrouplist")
authentication.authorize(request, 'groups:get_all')
# This shouldn't be necessary, but apparently is on mtn29
start = 0 if start is None else start
limit = 0 if limit is None else limit
try:
group_logic = GroupLogic()
result = group_logic.get_group_list_by_criteria(region, user,
starts_with,
contains,
start,
limit)
return result
except ErrorStatus as exception:
LOG.log_exception("GroupController - Failed to GetGrouplist", exception)
raise err_utils.get_error(request.transaction_id,
status_code=exception.status_code)
except Exception as exception:
LOG.log_exception("GroupController - Failed to GetGrouplist", exception)
raise err_utils.get_error(request.transaction_id,
status_code=500,
error_details=exception.message)
@wsexpose(None, str, rest_content_types='json', status_code=204)
def delete(self, group_id):
authentication.authorize(request, 'groups:delete')
group_logic = GroupLogic()
try:
LOG.info("GroupController - DeleteGroup: uuid is " + group_id)
group_logic.delete_group_by_uuid(group_id)
LOG.info("GroupController - DeleteGroup finished well")
event_details = 'Group {} deleted'.format(group_id)
utils.audit_trail('delete group', request.transaction_id,
request.headers, group_id,
event_details=event_details)
except ErrorStatus as exception:
LOG.log_exception("GroupController - Failed to DeleteGroup",
exception)
raise err_utils.get_error(request.transaction_id,
message=exception.message,
status_code=exception.status_code)
except Exception as exception:
LOG.log_exception("GroupController - Failed to DeleteGroup",
exception)
raise err_utils.get_error(request.transaction_id,
status_code=500,
error_details=exception.message)

View File

@ -2,6 +2,7 @@ from __future__ import absolute_import
from ..orm.configuration import ConfigurationController
from ..orm.customer.root import CustomerController
from ..orm.group.root import GroupController
from ..orm.logs import LogsController
from pecan.rest import RestController
@ -9,4 +10,5 @@ from pecan.rest import RestController
class OrmController(RestController):
configuration = ConfigurationController()
customers = CustomerController()
groups = GroupController()
logs = LogsController()

View File

@ -2,7 +2,9 @@ import logging
from orm.services.customer_manager.cms_rest.data.sql_alchemy.customer_record import CustomerRecord
from orm.services.customer_manager.cms_rest.data.sql_alchemy.customer_region_record import CustomerRegionRecord
from orm.services.customer_manager.cms_rest.data.sql_alchemy.group_record import GroupRecord
from orm.services.customer_manager.cms_rest.data.sql_alchemy.models import (CmsRole, CmsUser, Customer,
Groups,
CustomerRegion, Quota,
QuotaFieldDetail, Region,
UserRole)
@ -106,6 +108,13 @@ class DataManager(object):
return customer.first()
def get_group_by_uuid_or_name(self, grp):
group = self.session.query(Groups).filter(
or_(Groups.uuid == grp,
Groups.name == grp))
return group.first()
def get_quota_by_id(self, quota_id):
quota = self.session.query(Quota).filter(Quota.id == quota_id)
return quota.first()
@ -116,6 +125,11 @@ class DataManager(object):
self.customer_record = CustomerRecord(self.session)
return self.customer_record
if record_name == "Group" or record_name == "group":
if not hasattr(self, "group_record"):
self.group_record = GroupRecord(self.session)
return self.group_record
if record_name == "CustomerRegion" or record_name == "customer_region":
if not hasattr(self, "customer_region_record"):
self.customer_region_record = CustomerRegionRecord(
@ -193,6 +207,19 @@ class DataManager(object):
return sql_customer
def add_group(self, group, uuid):
sql_group = Groups(
uuid=uuid,
name=group.name,
domain_id=1,
description=group.description
)
self.session.add(sql_group)
self.flush()
return sql_group
def add_user_role(self, user_id, role_id, customer_id, region_id,
adding=False):
try:

View File

@ -0,0 +1,121 @@
from __builtin__ import int
from orm.services.customer_manager.cms_rest.data.sql_alchemy.models import (Groups)
from orm.services.customer_manager.cms_rest.logger import get_logger
LOG = get_logger(__name__)
class GroupRecord:
def __init__(self, session):
# this model is uses only for the parameters of access mothods, not an instance of model in the database
self.__groups = Groups()
self.__TableName = "groups"
if session:
self.setDBSession(session)
def setDBSession(self, session):
self.session = session
@property
def groups(self):
return self.__groups
@groups.setter
def groups(self, groups):
self.__groups = groups
def insert(self, groups):
try:
self.session.add(groups)
except Exception as exception:
LOG.log_exception("Failed to insert Group" + str(groups), exception)
raise
def delete_by_primary_key(self, group_id):
result = self.session.connection().execute("delete from groups where id = {}".format(group_id)) # nosec
return result
def read_by_primary_key(self):
return self.read_groups(self.__groups.id)
def read_groups(self, group_id):
try:
groups = self.session.query(Groups).filter(Groups.id == group_id)
return group.first()
except Exception as exception:
message = "Failed to read_groups:group_id: %d " % (group_id)
LOG.log_exception(message, exception)
raise
def read_group_by_uuid(self, group_uuid):
try:
groups = self.session.query(Groups).filter(Groups.uuid == group_uuid)
return groups.first()
except Exception as exception:
message = "Failed to read_group:group_uuid: %d " % group_uuid
LOG.log_exception(message, exception)
raise
def get_group_id_from_uuid(self, uuid):
result = self.session.connection().scalar("SELECT id from groups WHERE uuid = \"{}\"".format(uuid)) # nosec
if result:
return int(result)
else:
return None
def delete_group_by_uuid(self, uuid):
try:
result = self.session.query(Groups).filter(
Groups.uuid == uuid).delete()
return result
except Exception as exception:
message = "Failed to delete_group_by_uuid: uuid: {0}".format(uuid)
LOG.log_exception(message, exception)
raise
def get_groups_by_criteria(self, **criteria):
try:
LOG.info("get_groups_by_criteria: criteria: {0}".format(criteria))
region = criteria['region'] if 'region' in criteria else None
user = criteria['user'] if 'user' in criteria else None
rgroup = criteria['rgroup'] if 'rgroup' in criteria else None
starts_with = criteria['starts_with'] if 'starts_with' in criteria else None
contains = criteria['contains'] if 'contains' in criteria else None
query = self.session.query(Groups)
if starts_with:
query = query.filter(
Groups.name.ilike("{}%".format(starts_with)))
if contains:
query = query.filter(
Groups.name.ilike("%{}%".format(contains)))
query = self.customise_query(query, criteria)
return query.all()
except Exception as exception:
message = "Failed to get_groups_by_criteria: criteria: {0}".format(criteria)
LOG.log_exception(message, exception)
raise
def customise_query(self, query, kw):
start = int(kw['start']) if 'start' in kw else 0
limit = int(kw['limit']) if 'limit' in kw else 0
if start > 0:
query = query.offset(start)
if limit > 0:
query = query.limit(limit)
return query

View File

@ -1,4 +1,5 @@
from orm.services.customer_manager.cms_rest.data.sql_alchemy.base import Base
import orm.services.customer_manager.cms_rest.model.GroupModels as GroupWsmeModels
import orm.services.customer_manager.cms_rest.model.Models as WsmeModels
from oslo_db.sqlalchemy import models
@ -12,6 +13,72 @@ class CMSBaseModel(models.ModelBase):
__table_args__ = {'mysql_engine': 'InnoDB'}
'''
' CmsDomain is a DataObject and contains all the fields defined in cms_domain table record.
' defined as SqlAlchemy model map to a table
'''
class CmsDomain(Base, CMSBaseModel):
__tablename__ = 'cms_domain'
id = Column(Integer, primary_key=True)
name = Column(String(64), nullable=False)
def __json__(self):
return dict(
id=self.id,
name=self.name
)
'''
' Groups is a DataObject and contains all the fields defined in Groups table record.
' defined as SqlAlchemy model map to a table
'''
class Groups(Base, CMSBaseModel):
__tablename__ = 'groups'
id = Column(Integer, primary_key=True)
uuid = Column(String(64), nullable=False, unique=True)
domain_id = Column(Integer, ForeignKey('cms_domain.id'), primary_key=True, nullable=False)
name = Column(String(64), nullable=False, unique=True)
description = Column(String(255), nullable=True)
def __json__(self):
return dict(
uuid=self.uuid,
name=self.name,
description=self.description,
domain_id=self.domain_id
)
def get_dict(self):
return self.__json__()
def get_proxy_dict(self):
proxy_dict = {
"uuid": self.uuid,
"name": self.name,
"domain_id": self.domain_id,
"description": self.description
}
return proxy_dict
def to_wsme(self):
uuid = self.uuid
name = self.name
domainId = self.domain_id
description = self.description
result = GroupWsmeModels.Group(description=description,
name=name,
uuid=uuid,
domainId=domainId)
return result
'''
' CmsUser is a DataObject and contains all the fields defined in CmsUser table record.

View File

@ -35,5 +35,12 @@
"customers:add_metadata": "rule:admin_or_support_or_creator",
"customers:update_metadata": "rule:admin_or_creator",
"customers:enable": "rule:admin_or_support_or_creator"
}
"customers:enable": "rule:admin_or_support_or_creator",
"groups:get_one": "rule:admin_or_support_or_viewer_or_creator",
"groups:get_all": "rule:admin_or_support_or_viewer_or_creator",
"groups:create": "rule:admin_or_support_or_creator",
"groups:update": "rule:admin_or_creator",
"groups:delete": "rule:admin"
}

View File

@ -0,0 +1,145 @@
from pecan import request
from orm.common.orm_common.utils import utils
from orm.services.customer_manager.cms_rest.data.data_manager import DataManager
from orm.services.customer_manager.cms_rest.logger import get_logger
from orm.services.customer_manager.cms_rest.logic.error_base import (ErrorStatus)
from orm.services.customer_manager.cms_rest.model.GroupModels import (GroupResultWrapper, GroupSummary,
GroupSummaryResponse)
LOG = get_logger(__name__)
class GroupLogic(object):
def build_full_group(self, group, uuid, datamanager):
if any(char in ":" for char in group.name):
raise ErrorStatus(400, "Group Name does not allow colon(:).")
if group.name.strip() == '':
raise ErrorStatus(400, "Group Name can not be blank.")
sql_group = datamanager.add_group(group, uuid)
return sql_group
def create_group(self, group, uuid, transaction_id):
datamanager = DataManager()
try:
group.handle_region_group()
sql_group = self.build_full_group(group, uuid, datamanager)
group_result_wrapper = build_response(uuid, transaction_id, 'create')
datamanager.commit()
except Exception as exp:
LOG.log_exception("GroupLogic - Failed to CreateGroup", exp)
datamanager.rollback()
raise
return group_result_wrapper
def update_group(self, group, group_uuid, transaction_id):
datamanager = DataManager()
try:
group.validate_model('update')
group_record = datamanager.get_record('group')
group_id = group_record.get_group_id_from_uuid(
group_uuid)
sql_group = group_record.read_group_by_uuid(group_uuid)
if not sql_group:
raise ErrorStatus(404, 'group {0} was not found'.format(group_uuid))
old_group_dict = sql_group.get_proxy_dict()
group_record.delete_by_primary_key(group_id)
datamanager.flush()
sql_group = self.build_full_group(group, group_uuid,
datamanager)
new_group_dict = sql_group.get_proxy_dict()
group_result_wrapper = build_response(group_uuid, transaction_id, 'update')
datamanager.flush() # i want to get any exception created by this insert
datamanager.commit()
return group_result_wrapper
except Exception as exp:
LOG.log_exception("GroupLogic - Failed to CreateGroup", exp)
datamanager.rollback()
raise
def get_group(self, group):
datamanager = DataManager()
sql_group = datamanager.get_group_by_uuid_or_name(group)
if not sql_group:
raise ErrorStatus(404, 'group: {0} not found'.format(group))
ret_group = sql_group.to_wsme()
ret_group.status = 'no regions'
return ret_group
def get_group_list_by_criteria(self, region, user, starts_with, contains,
start=0, limit=0):
datamanager = DataManager()
group_record = datamanager.get_record('group')
sql_groups = group_record.get_groups_by_criteria(region=region,
user=user,
starts_with=starts_with,
contains=contains,
start=start,
limit=limit)
response = GroupSummaryResponse()
if sql_groups:
for sql_group in sql_groups:
groups = GroupSummary.from_db_model(sql_group)
response.groups.append(groups)
return response
def delete_group_by_uuid(self, group_id):
datamanager = DataManager()
try:
datamanager.begin_transaction()
group_record = datamanager.get_record('group')
sql_group = group_record.read_group_by_uuid(group_id)
if sql_group is None:
raise ErrorStatus(404, "Group '{0}' not found".format(group_id))
# OK to delete
group_record.delete_group_by_uuid(group_id)
datamanager.flush() # i want to get any exception created by this delete
datamanager.commit()
except Exception as exp:
LOG.log_exception("GroupLogic - Failed to delete group", exp)
datamanager.rollback()
raise
def build_response(group_uuid, transaction_id, context):
"""this function generate th group action response JSON
:param group_uuid:
:param transaction_id:
:param context: create or update
:return:
"""
# The link should point to the group itself (/v1/orm/groups/{id})
link_elements = request.url.split('/')
base_link = '/'.join(link_elements)
if context == 'create':
base_link = base_link + '/' + group_uuid
timestamp = utils.get_time_human()
group_result_wrapper = GroupResultWrapper(
transaction_id=transaction_id,
id=group_uuid,
updated=None,
created=timestamp,
links={'self': base_link})
return group_result_wrapper

View File

@ -0,0 +1,125 @@
from orm.services.customer_manager.cms_rest.logic.error_base import ErrorStatus
from orm.services.customer_manager.cms_rest.model.Model import Model
import wsme
from wsme import types as wtypes
class Region(Model):
"""network model the group
"""
def __init__(self, id):
self.id = id
class Group(Model):
"""group entity with all it's related data
"""
description = wsme.wsattr(wsme.types.text, mandatory=True)
name = wsme.wsattr(wsme.types.text, mandatory=True)
status = wsme.wsattr(wsme.types.text, mandatory=False)
domainId = wsme.wsattr(int, mandatory=True)
uuid = wsme.wsattr(wsme.types.text, mandatory=False)
regions = wsme.wsattr([Region], mandatory=False)
def __init__(self, description="", name="",
regions=[], status="", domainId=1, uuid=None):
"""Create a new Group.
:param description: Server name
:param status: status of creation
"""
self.description = description
self.name = name
self.status = status
self.domainId = domainId
self.regions = regions
if uuid is not None:
self.uuid = uuid
def validate_model(self, context=None):
"""this function check if the group model meet the demands
:param context: i.e. 'create 'update'
:return: none
"""
if context == "update":
for region in self.regions:
if region.type == "group":
raise ErrorStatus(400, "region type is invalid for update, \'group\' can be only in create")
def handle_region_group(self):
regions_to_add = []
for region in self.regions[:]: # get copy of it to be able to delete from the origin
if region.type == "group":
group_regions = self.get_regions_for_group(region.name)
if not group_regions:
raise ErrorStatus(404, 'Group {} Not found'.format(region.name))
self.regions.remove(region)
self.regions.extend(set(regions_to_add)) # remove duplicates if exist
class GroupResult(Model):
id = wsme.wsattr(wsme.types.text, mandatory=True)
updated = wsme.wsattr(wsme.types.text, mandatory=False)
created = wsme.wsattr(wsme.types.text, mandatory=False)
links = wsme.wsattr({str: str}, mandatory=True)
def __init__(self, id, links={}, updated=None, created=None):
self.id = id
if updated:
self.updated = updated
elif created:
self.created = created
self.links = links
class GroupResultWrapper(Model):
transaction_id = wsme.wsattr(wsme.types.text, mandatory=True)
group = wsme.wsattr(GroupResult, mandatory=True)
def __init__(self, transaction_id, id, links, updated, created):
group_result = GroupResult(id, links, updated, created)
self.transaction_id = transaction_id
self.group = group_result
""" GroupSummary is a DataObject and contains all the fields defined in GroupSummary structure. """
class GroupSummary(Model):
name = wsme.wsattr(wsme.types.text)
id = wsme.wsattr(wsme.types.text)
description = wsme.wsattr(wsme.types.text)
domain_id = wsme.wsattr(int, mandatory=True)
status = wsme.wsattr(wtypes.text, mandatory=True)
def __init__(self, name='', id='', description='',
status="", domain_id=0):
Model.__init__(self)
self.name = name
self.id = id
self.description = description
self.status = status
self.domain_id = domain_id
@staticmethod
def from_db_model(sql_group):
group = GroupSummary()
group.id = sql_group.uuid
group.name = sql_group.name
group.description = sql_group.description
group.domain_id = sql_group.domain_id
return group
class GroupSummaryResponse(Model):
groups = wsme.wsattr([GroupSummary], mandatory=True)
def __init__(self):
Model.__init__(self)
self.groups = []
""" ****************************************************************** """

View File

@ -94,3 +94,65 @@ create or replace view rds_resource_status_view AS
(
SELECT id, resource_id, region, status,
err_code, operation from resource_status);
create table if not exists cms_domain
(
id integer auto_increment not null,
name varchar(64) not null,
primary key (id),
unique name_idx (name));
create table if not exists groups
(
id integer auto_increment not null,
uuid varchar(64) not null,
domain_id integer not null,
name varchar(64) not null,
description varchar(255) not null,
primary key (id),
foreign key (`domain_id`) references `cms_domain` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION,
unique uuid_idx (uuid));
create table if not exists group_region
(
region_id integer not null,
group_id integer not null,
primary key (region_id, group_id),
foreign key (`region_id`) references `cms_region` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION,
foreign key (`group_id`) references `groups` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION,
index region_id (region_id),
index group_id_idx (group_id));
create table if not exists group_role
(
role_id integer not null,
group_id integer not null,
region_id integer not null,
primary key (role_id, region_id, group_id),
foreign key (role_id) references cms_role(id),
foreign key (`group_id`) references `groups` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION,
index region_id (region_id),
index group_id_idx (group_id));
create table if not exists group_user
(
group_id integer not null,
user_id integer not null,
primary key (group_id, user_id),
foreign key (`user_id`) references `cms_user` (`id`) ON DELETE CASCADE,
foreign key (`group_id`) references `groups` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION,
index user_id (user_id),
index group_id (group_id));
create table if not exists group_customer
(
group_id integer not null,
customer_id integer not null,
region_id integer not null,
primary key (group_id, customer_id, region_id),
foreign key (`group_id`) references `groups` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION,
foreign key (`customer_id`) references `customer` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION,
foreign key (`region_id`) references `cms_region` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION,
index customer_id_idx (customer_id),
index regio_id_idx (region_id));