Merge "policy library DB interface, DSE service, API"
This commit is contained in:
commit
a724dfb59c
|
@ -20,6 +20,7 @@ from __future__ import absolute_import
|
|||
from oslo_config import cfg
|
||||
|
||||
ENGINE_SERVICE_ID = '__engine'
|
||||
LIBRARY_SERVICE_ID = '__library'
|
||||
DS_MANAGER_SERVICE_ID = '_ds_manager'
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,223 @@
|
|||
# Copyright (c) 2017 VMware, Inc. All rights reserved.
|
||||
#
|
||||
# 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 __future__ import print_function
|
||||
from __future__ import division
|
||||
from __future__ import absolute_import
|
||||
|
||||
import json
|
||||
import jsonschema
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
from congress.api import base
|
||||
from congress.api import error_codes
|
||||
from congress.api import webservice
|
||||
from congress import exception
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class LibraryPolicyModel(base.APIModel):
|
||||
"""Model for handling API requests about Library Policies."""
|
||||
|
||||
# Note(thread-safety): blocking function
|
||||
def get_items(self, params, context=None):
|
||||
"""Get items in model.
|
||||
|
||||
Args:
|
||||
params: A dict-like object containing parameters
|
||||
from the request query string and body.
|
||||
context: Key-values providing frame of reference of request
|
||||
|
||||
Returns: A dict containing at least a 'results' key whose value is
|
||||
a list of items in the model. Additional keys set in the
|
||||
dict will also be rendered for the user.
|
||||
"""
|
||||
try:
|
||||
# Note(thread-safety): blocking call
|
||||
return {"results": self.invoke_rpc(base.LIBRARY_SERVICE_ID,
|
||||
'get_policies',
|
||||
{})}
|
||||
except exception.CongressException as e:
|
||||
raise webservice.DataModelException.create(e)
|
||||
|
||||
# Note(thread-safety): blocking function
|
||||
def get_item(self, id_, params, context=None):
|
||||
"""Retrieve item with name name from model.
|
||||
|
||||
Args:
|
||||
name: The unique name of the item to retrieve
|
||||
params: A dict-like object containing parameters
|
||||
from the request query string and body.
|
||||
context: Key-values providing frame of reference of request
|
||||
|
||||
Returns:
|
||||
The matching item or None if no item named name exists.
|
||||
"""
|
||||
try:
|
||||
# Note(thread-safety): blocking call
|
||||
return self.invoke_rpc(base.LIBRARY_SERVICE_ID,
|
||||
'get_policy',
|
||||
{'id_': id_, 'include_rules': True})
|
||||
except exception.CongressException as e:
|
||||
raise webservice.DataModelException.create(e)
|
||||
|
||||
# Note(thread-safety): blocking function
|
||||
def add_item(self, item, params, id_=None, context=None):
|
||||
"""Add item to model.
|
||||
|
||||
Args:
|
||||
item: The item to add to the model
|
||||
params: A dict-like object containing parameters
|
||||
from the request query string and body.
|
||||
id_: The unique name of the item
|
||||
context: Key-values providing frame of reference of request
|
||||
|
||||
Returns:
|
||||
Tuple of (ID, newly_created_item)
|
||||
|
||||
Raises:
|
||||
KeyError: ID already exists.
|
||||
DataModelException: Addition cannot be performed.
|
||||
"""
|
||||
if id_ is not None:
|
||||
(num, desc) = error_codes.get('policy_id_must_not_be_provided')
|
||||
raise webservice.DataModelException(num, desc)
|
||||
|
||||
self._validate_policy_item(item)
|
||||
try:
|
||||
# Note(thread-safety): blocking call
|
||||
policy_metadata = self.invoke_rpc(
|
||||
base.LIBRARY_SERVICE_ID, 'create_policy',
|
||||
{'policy_dict': item})
|
||||
except exception.CongressException as e:
|
||||
raise webservice.DataModelException.create(e)
|
||||
|
||||
return (policy_metadata['id'], policy_metadata)
|
||||
|
||||
# Note(thread-safety): blocking function
|
||||
def delete_item(self, id_, params, context=None):
|
||||
"""Remove item from model.
|
||||
|
||||
Args:
|
||||
id_: The unique name of the item to be removed
|
||||
params:
|
||||
context: Key-values providing frame of reference of request
|
||||
|
||||
Returns:
|
||||
The removed item.
|
||||
|
||||
Raises:
|
||||
KeyError: Item with specified id_ not present.
|
||||
"""
|
||||
# Note(thread-safety): blocking call
|
||||
return self.invoke_rpc(base.LIBRARY_SERVICE_ID,
|
||||
'delete_policy',
|
||||
{'id_': id_})
|
||||
|
||||
def update_item(self, id_, item, params, context=None):
|
||||
"""Update item with id_ with new data.
|
||||
|
||||
Args:
|
||||
id_: The ID of the item to be updated
|
||||
item: The new item
|
||||
params: A dict-like object containing parameters
|
||||
from the request query string and body.
|
||||
context: Key-values providing frame of reference of request
|
||||
|
||||
Returns:
|
||||
The updated item.
|
||||
|
||||
Raises:
|
||||
KeyError: Item with specified id_ not present.
|
||||
"""
|
||||
self._validate_policy_item(item)
|
||||
|
||||
# Note(thread-safety): blocking call
|
||||
try:
|
||||
return self.invoke_rpc(base.LIBRARY_SERVICE_ID,
|
||||
'replace_policy',
|
||||
{'id_': id_,
|
||||
'policy_dict': item})
|
||||
except exception.CongressException as e:
|
||||
raise webservice.DataModelException.create(e)
|
||||
|
||||
def _validate_policy_item(self, item):
|
||||
schema_json = '''
|
||||
{
|
||||
"id": "PolicyProperties",
|
||||
"title": "Policy Properties",
|
||||
"type": "object",
|
||||
"required": ["name", "rules"],
|
||||
"properties": {
|
||||
"name": {
|
||||
"title": "Policy unique name",
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 255
|
||||
},
|
||||
"description": {
|
||||
"title": "Policy description",
|
||||
"type": "string"
|
||||
},
|
||||
"kind": {
|
||||
"title": "Policy kind",
|
||||
"type": "string",
|
||||
"enum": ["database", "nonrecursive", "action", "materialized",
|
||||
"delta", "datasource"]
|
||||
},
|
||||
"abbreviation": {
|
||||
"title": "Policy name abbreviation",
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 5
|
||||
},
|
||||
"rules": {
|
||||
"title": "collection of rules",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"PolicyRule": {
|
||||
"title": "Policy rule",
|
||||
"type": "object",
|
||||
"required": ["rule"],
|
||||
"properties": {
|
||||
"rule": {
|
||||
"title": "Rule definition following policy grammar",
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"title": "User-friendly name",
|
||||
"type": "string"
|
||||
},
|
||||
"comment": {
|
||||
"title": "User-friendly comment",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
'''
|
||||
try:
|
||||
jsonschema.validate(item, json.loads(schema_json))
|
||||
except jsonschema.exceptions.ValidationError as ve:
|
||||
raise webservice.DataModelException(
|
||||
1000, 'Input item violates JSON Schema', data=str(ve))
|
|
@ -45,6 +45,21 @@ class APIRouterV1(object):
|
|||
allow_replace=False)
|
||||
resource_mgr.register_handler(policy_element_handler)
|
||||
|
||||
library_policies = process_dict['api-library-policy']
|
||||
|
||||
library_policy_collection_handler = webservice.CollectionHandler(
|
||||
r'/v1/librarypolicies',
|
||||
library_policies)
|
||||
resource_mgr.register_handler(library_policy_collection_handler)
|
||||
library_policy_path = r'/v1/librarypolicies/(?P<policy_id>[^/]+)'
|
||||
library_policy_element_handler = webservice.ElementHandler(
|
||||
library_policy_path,
|
||||
library_policies,
|
||||
library_policy_collection_handler,
|
||||
allow_update=False,
|
||||
allow_replace=True)
|
||||
resource_mgr.register_handler(library_policy_element_handler)
|
||||
|
||||
policy_rules = process_dict['api-rule']
|
||||
rule_collection_handler = webservice.CollectionHandler(
|
||||
r'/v1/policies/(?P<policy_id>[^/]+)/rules',
|
||||
|
|
|
@ -26,6 +26,7 @@ import json
|
|||
import re
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_db import exception as db_exc
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import uuidutils
|
||||
import six
|
||||
|
@ -249,17 +250,21 @@ class ElementHandler(AbstractApiHandler):
|
|||
Returns:
|
||||
A webob response object.
|
||||
"""
|
||||
if request.method == 'GET' and self.allow_read:
|
||||
return self.read(request)
|
||||
elif request.method == 'POST' and self.allow_actions:
|
||||
return self.action(request)
|
||||
elif request.method == 'PUT' and self.allow_replace:
|
||||
return self.replace(request)
|
||||
elif request.method == 'PATCH' and self.allow_update:
|
||||
return self.update(request)
|
||||
elif request.method == 'DELETE' and self.allow_delete:
|
||||
return self.delete(request)
|
||||
return NOT_SUPPORTED_RESPONSE
|
||||
try:
|
||||
if request.method == 'GET' and self.allow_read:
|
||||
return self.read(request)
|
||||
elif request.method == 'POST' and self.allow_actions:
|
||||
return self.action(request)
|
||||
elif request.method == 'PUT' and self.allow_replace:
|
||||
return self.replace(request)
|
||||
elif request.method == 'PATCH' and self.allow_update:
|
||||
return self.update(request)
|
||||
elif request.method == 'DELETE' and self.allow_delete:
|
||||
return self.delete(request)
|
||||
return NOT_SUPPORTED_RESPONSE
|
||||
except db_exc.DBError:
|
||||
LOG.exception('Database backend experienced an unknown error.')
|
||||
raise exception.DatabaseError
|
||||
|
||||
def read(self, request):
|
||||
if not hasattr(self.model, 'get_item'):
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
# Copyright (c) 2017 VMware, Inc. All rights reserved.
|
||||
#
|
||||
# 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 __future__ import print_function
|
||||
from __future__ import division
|
||||
from __future__ import absolute_import
|
||||
|
||||
import json
|
||||
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.orm import exc as db_exc
|
||||
|
||||
from congress.db import api as db
|
||||
from congress.db import model_base
|
||||
|
||||
|
||||
class LibraryPolicy(model_base.BASE, model_base.HasId):
|
||||
__tablename__ = 'librarypolicies'
|
||||
|
||||
name = sa.Column(sa.String(255), nullable=False)
|
||||
abbreviation = sa.Column(sa.String(5), nullable=False)
|
||||
description = sa.Column(sa.Text(), nullable=False)
|
||||
kind = sa.Column(sa.Text(), nullable=False)
|
||||
rules = sa.Column(sa.Text(), nullable=False)
|
||||
|
||||
def to_dict(self, include_rules=True, json_rules=False):
|
||||
"""From a given library policy, return a policy dict.
|
||||
|
||||
Args:
|
||||
include_rules (bool, optional): include policy rules in return
|
||||
dictionary. Defaults to False.
|
||||
"""
|
||||
if not include_rules:
|
||||
d = {'id': self.id,
|
||||
'name': self.name,
|
||||
'abbreviation': self.abbreviation,
|
||||
'description': self.description,
|
||||
'kind': self.kind}
|
||||
else:
|
||||
d = {'id': self.id,
|
||||
'name': self.name,
|
||||
'abbreviation': self.abbreviation,
|
||||
'description': self.description,
|
||||
'kind': self.kind,
|
||||
'rules': (self.rules if json_rules
|
||||
else json.loads(self.rules))}
|
||||
return d
|
||||
|
||||
|
||||
def add_policy(policy_dict, session=None):
|
||||
session = session or db.get_session()
|
||||
with session.begin(subtransactions=True):
|
||||
new_row = LibraryPolicy(
|
||||
name=policy_dict['name'],
|
||||
abbreviation=policy_dict['abbreviation'],
|
||||
description=policy_dict['description'],
|
||||
kind=policy_dict['kind'],
|
||||
rules=json.dumps(policy_dict['rules']))
|
||||
session.add(new_row)
|
||||
return new_row
|
||||
|
||||
|
||||
def replace_policy(id_, policy_dict, session=None):
|
||||
session = session or db.get_session()
|
||||
try:
|
||||
with session.begin(subtransactions=True):
|
||||
new_row = LibraryPolicy(
|
||||
id=id_,
|
||||
name=policy_dict['name'],
|
||||
abbreviation=policy_dict['abbreviation'],
|
||||
description=policy_dict['description'],
|
||||
kind=policy_dict['kind'],
|
||||
rules=json.dumps(policy_dict['rules']))
|
||||
session.query(LibraryPolicy).filter(
|
||||
LibraryPolicy.id == id_).one().update(
|
||||
new_row.to_dict(include_rules=True, json_rules=True))
|
||||
return new_row
|
||||
except db_exc.NoResultFound:
|
||||
raise KeyError('No policy found with policy id %s' % id_)
|
||||
|
||||
|
||||
def delete_policy(id_, session=None):
|
||||
session = session or db.get_session()
|
||||
return session.query(LibraryPolicy).filter(
|
||||
LibraryPolicy.id == id_).delete()
|
||||
|
||||
|
||||
def delete_policies(session=None):
|
||||
session = session or db.get_session()
|
||||
return session.query(LibraryPolicy).delete()
|
||||
|
||||
|
||||
def get_policy(id_, session=None):
|
||||
session = session or db.get_session()
|
||||
try:
|
||||
return session.query(LibraryPolicy).filter(
|
||||
LibraryPolicy.id == id_).one()
|
||||
except db_exc.NoResultFound:
|
||||
raise KeyError('No policy found with policy id %s' % id_)
|
||||
|
||||
|
||||
def get_policies(session=None):
|
||||
session = session or db.get_session()
|
||||
return (session.query(LibraryPolicy).all())
|
|
@ -1 +1 @@
|
|||
3cee191c4f84
|
||||
c0125080d572
|
|
@ -0,0 +1,47 @@
|
|||
# Copyright 2017 OpenStack Foundation
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
"""policy library
|
||||
|
||||
Revision ID: c0125080d572
|
||||
Revises: aabe895bbd4d
|
||||
Create Date: 2017-06-21 13:20:14.529313
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'c0125080d572'
|
||||
down_revision = 'aabe895bbd4d'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.create_table(
|
||||
'librarypolicies',
|
||||
sa.Column('id', sa.String(length=36), nullable=False),
|
||||
sa.Column('name', sa.String(length=255), nullable=False),
|
||||
sa.Column('abbreviation', sa.String(length=5), nullable=False),
|
||||
sa.Column('description', sa.Text(), nullable=False),
|
||||
sa.Column('kind', sa.Text(), nullable=False),
|
||||
sa.Column('rules', sa.Text(), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
mysql_engine='InnoDB'
|
||||
)
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_table('librarypolicies')
|
|
@ -21,6 +21,7 @@ import eventlet
|
|||
eventlet.monkey_patch() # for using oslo.messaging w/ eventlet executor
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_db import exception as db_exc
|
||||
from oslo_log import log as logging
|
||||
import oslo_messaging as messaging
|
||||
from oslo_messaging import exceptions as messaging_exceptions
|
||||
|
@ -103,7 +104,8 @@ class DseNode(object):
|
|||
self.context = self._message_context()
|
||||
self.transport = messaging.get_transport(
|
||||
self.messaging_config,
|
||||
allowed_remote_exmods=[exception.__name__, dispatcher.__name__, ])
|
||||
allowed_remote_exmods=[exception.__name__, dispatcher.__name__,
|
||||
db_exc.__name__, ])
|
||||
self._rpctarget = self.node_rpc_target(self.node_id, self.node_id)
|
||||
self._rpc_server = messaging.get_rpc_server(
|
||||
self.transport, self._rpctarget, self.node_rpc_endpoints,
|
||||
|
|
|
@ -180,6 +180,10 @@ class PolicyRuntimeException(CongressException):
|
|||
pass
|
||||
|
||||
|
||||
class DatabaseError(CongressException):
|
||||
msg_fmt = _("Database backend experienced an unknown error.")
|
||||
|
||||
|
||||
class IncompleteSchemaException(CongressException):
|
||||
pass
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ from congress.api import action_model
|
|||
from congress.api import application
|
||||
from congress.api import base as api_base
|
||||
from congress.api import datasource_model
|
||||
from congress.api import library_policy_model
|
||||
from congress.api import policy_model
|
||||
from congress.api import router
|
||||
from congress.api import row_model
|
||||
|
@ -38,9 +39,9 @@ from congress.db import datasources as db_datasources
|
|||
from congress.dse2 import datasource_manager as ds_manager
|
||||
from congress.dse2 import dse_node
|
||||
from congress import exception
|
||||
from congress.library_service import library_service
|
||||
from congress.policy_engines import agnostic
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
@ -88,6 +89,14 @@ def create2(node_id=None, bus_id=None, existing_node=None,
|
|||
node.register_service(engine)
|
||||
initialize_policy_engine(engine)
|
||||
|
||||
# NOTE(ekcs): library service does not depend on policy engine,
|
||||
# it is placed on the same nodes as policy engine for convenience only
|
||||
LOG.info("Registering congress policy library service on node %s",
|
||||
node.node_id)
|
||||
library = create_policy_library_service()
|
||||
services[api_base.LIBRARY_SERVICE_ID] = library
|
||||
node.register_service(library)
|
||||
|
||||
if api:
|
||||
LOG.info("Registering congress API service on node %s", node.node_id)
|
||||
services['api'], services['api_service'] = create_api()
|
||||
|
@ -108,6 +117,8 @@ def create_api():
|
|||
def create_api_models(bus):
|
||||
"""Create all the API models and return as a dictionary for DSE2."""
|
||||
res = {}
|
||||
res['api-library-policy'] = library_policy_model.LibraryPolicyModel(
|
||||
'api-library-policy', bus=bus)
|
||||
res['api-policy'] = policy_model.PolicyModel('api-policy', bus=bus)
|
||||
res['api-rule'] = rule_model.RuleModel('api-rule', bus=bus)
|
||||
res['api-row'] = row_model.RowModel('api-row', bus=bus)
|
||||
|
@ -137,6 +148,12 @@ def initialize_policy_engine(engine):
|
|||
engine.persistent_load_rules()
|
||||
|
||||
|
||||
def create_policy_library_service():
|
||||
"""Create policy library service."""
|
||||
library = library_service.LibraryService(api_base.LIBRARY_SERVICE_ID)
|
||||
return library
|
||||
|
||||
|
||||
def create_datasources(bus):
|
||||
"""Create and register datasource services ."""
|
||||
if cfg.CONF.delete_missing_driver_datasources:
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
# Copyright (c) 2017 VMware, Inc. All rights reserved.
|
||||
#
|
||||
# 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 __future__ import print_function
|
||||
from __future__ import division
|
||||
from __future__ import absolute_import
|
||||
|
||||
import copy
|
||||
|
||||
from oslo_db import exception as db_exc
|
||||
from oslo_log import log as logging
|
||||
|
||||
from congress.datalog import compile
|
||||
from congress.db import db_library_policies
|
||||
from congress.dse2 import data_service
|
||||
from congress import exception
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class LibraryService (data_service.DataService):
|
||||
def __init__(self, name):
|
||||
data_service.DataService.__init__(self, name)
|
||||
self.name = name
|
||||
self.add_rpc_endpoint(DseLibraryServiceEndpoints(self))
|
||||
|
||||
def create_policy(self, policy_dict):
|
||||
policy_dict = copy.deepcopy(policy_dict)
|
||||
policy_name = policy_dict['name']
|
||||
|
||||
# check name is valid
|
||||
if not compile.string_is_servicename(policy_name):
|
||||
raise exception.PolicyException(
|
||||
'name `%s` is not a valid policy name' % policy_name)
|
||||
|
||||
# make defaults
|
||||
if 'kind' not in policy_dict:
|
||||
policy_dict['kind'] = 'nonrecursive'
|
||||
if 'abbreviation' not in policy_dict:
|
||||
policy_dict['abbreviation'] = policy_name[:5]
|
||||
if 'description' not in policy_dict:
|
||||
policy_dict['description'] = ''
|
||||
|
||||
try:
|
||||
# Note(thread-safety): blocking call
|
||||
policy = db_library_policies.add_policy(policy_dict=policy_dict)
|
||||
return policy.to_dict()
|
||||
except db_exc.DBError:
|
||||
LOG.exception('Creating a new library policy failed.')
|
||||
raise
|
||||
|
||||
def get_policies(self, include_rules=True):
|
||||
return [p.to_dict(include_rules)
|
||||
for p in db_library_policies.get_policies()]
|
||||
|
||||
def get_policy(self, id_, include_rules=True):
|
||||
# Note(thread-safety): blocking call
|
||||
policy = db_library_policies.get_policy(id_)
|
||||
return policy.to_dict(include_rules)
|
||||
|
||||
def delete_all_policies(self):
|
||||
# Note(thread-safety): blocking call
|
||||
db_library_policies.delete_policies()
|
||||
|
||||
def delete_policy(self, id_):
|
||||
# Note(thread-safety): blocking call
|
||||
db_object = db_library_policies.get_policy(id_)
|
||||
db_library_policies.delete_policy(id_)
|
||||
return db_object.to_dict(include_rules=True)
|
||||
|
||||
def replace_policy(self, id_, policy_dict):
|
||||
policy_name = policy_dict['name']
|
||||
|
||||
# check name is valid
|
||||
if not compile.string_is_servicename(policy_name):
|
||||
raise exception.PolicyException(
|
||||
"Policy name %s is not a valid service name" % policy_name)
|
||||
|
||||
# make defaults
|
||||
if 'kind' not in policy_dict:
|
||||
policy_dict['kind'] = 'nonrecursive'
|
||||
if 'abbreviation' not in policy_dict:
|
||||
policy_dict['abbreviation'] = policy_name[:5]
|
||||
if 'description' not in policy_dict:
|
||||
policy_dict['description'] = ''
|
||||
|
||||
# Note(thread-safety): blocking call
|
||||
policy = db_library_policies.replace_policy(
|
||||
id_, policy_dict=policy_dict)
|
||||
return policy.to_dict()
|
||||
|
||||
|
||||
class DseLibraryServiceEndpoints(object):
|
||||
"""RPC endpoints exposed by LibraryService."""
|
||||
|
||||
def __init__(self, data_service):
|
||||
self.data_service = data_service
|
||||
|
||||
def create_policy(
|
||||
self, context, policy_dict):
|
||||
return self.data_service.create_policy(
|
||||
policy_dict)
|
||||
|
||||
def get_policies(self, context, include_rules=True):
|
||||
return self.data_service.get_policies(include_rules)
|
||||
|
||||
def get_policy(self, context, id_, include_rules=True):
|
||||
return self.data_service.get_policy(id_, include_rules)
|
||||
|
||||
def delete_all_policies(self, context):
|
||||
return self.data_service.delete_all_policies()
|
||||
|
||||
def delete_policy(self, context, id_):
|
||||
return self.data_service.delete_policy(id_)
|
||||
|
||||
def replace_policy(self, context, id_, policy_dict):
|
||||
return self.data_service.replace_policy(id_, policy_dict)
|
|
@ -60,13 +60,15 @@ def setup_config(with_fake_datasource=True, node_id='testnode',
|
|||
node.register_service(data)
|
||||
|
||||
engine_service = None
|
||||
library_service = None
|
||||
api_service = None
|
||||
if policy:
|
||||
engine_service = services[api_base.ENGINE_SERVICE_ID]
|
||||
library_service = services[api_base.LIBRARY_SERVICE_ID]
|
||||
if api:
|
||||
api_service = services['api']
|
||||
if datasources:
|
||||
ds_manager = services['ds_manager']
|
||||
|
||||
return {'node': node, 'engine': engine_service, 'data': data,
|
||||
'api': api_service, 'ds_manager': ds_manager}
|
||||
return {'node': node, 'engine': engine_service, 'library': library_service,
|
||||
'data': data, 'api': api_service, 'ds_manager': ds_manager}
|
||||
|
|
|
@ -0,0 +1,250 @@
|
|||
# Copyright (c) 2017 VMware Inc
|
||||
#
|
||||
# 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 __future__ import print_function
|
||||
from __future__ import division
|
||||
from __future__ import absolute_import
|
||||
|
||||
import copy
|
||||
|
||||
from congress.api import webservice
|
||||
from congress.tests.api import base as api_base
|
||||
from congress.tests import base
|
||||
|
||||
|
||||
class TestLibraryPolicyModel(base.SqlTestCase):
|
||||
def setUp(self):
|
||||
super(TestLibraryPolicyModel, self).setUp()
|
||||
|
||||
services = api_base.setup_config()
|
||||
self.library_policy_model = services['api']['api-library-policy']
|
||||
self.node = services['node']
|
||||
self.engine = services['engine']
|
||||
self._add_test_policy()
|
||||
|
||||
def _add_test_policy(self):
|
||||
test_policy = {
|
||||
"name": "test_policy",
|
||||
"description": "test policy description",
|
||||
"kind": "nonrecursive",
|
||||
"abbreviation": "abbr",
|
||||
"rules": [{"rule": "p(x) :- q(x)", "comment": "test comment",
|
||||
"name": "test name"},
|
||||
{"rule": "p(x) :- q2(x)", "comment": "test comment2",
|
||||
"name": "test name2"}]
|
||||
}
|
||||
test_policy_id, obj = self.library_policy_model.add_item(
|
||||
test_policy, {})
|
||||
test_policy["id"] = test_policy_id
|
||||
|
||||
test_policy2 = {
|
||||
"name": "test_policy2",
|
||||
"description": "test policy2 description",
|
||||
"kind": "nonrecursive",
|
||||
"abbreviation": "abbr2",
|
||||
"rules": []
|
||||
}
|
||||
test_policy_id, obj = self.library_policy_model.add_item(
|
||||
test_policy2, {})
|
||||
test_policy2["id"] = test_policy_id
|
||||
|
||||
self.policy = test_policy
|
||||
self.policy2 = test_policy2
|
||||
|
||||
self.policy_metadata = copy.deepcopy(test_policy)
|
||||
self.policy2_metadata = copy.deepcopy(test_policy2)
|
||||
del self.policy_metadata['rules']
|
||||
del self.policy2_metadata['rules']
|
||||
|
||||
def test_get_items(self):
|
||||
ret = self.library_policy_model.get_items({})
|
||||
self.assertTrue(all(p in ret['results']
|
||||
for p in [self.policy,
|
||||
self.policy2]))
|
||||
|
||||
def test_get_item(self):
|
||||
expected_ret = self.policy
|
||||
ret = self.library_policy_model.get_item(self.policy["id"], {})
|
||||
self.assertEqual(expected_ret, ret)
|
||||
|
||||
def test_get_invalid_item(self):
|
||||
self.assertRaises(KeyError,
|
||||
self.library_policy_model.get_item,
|
||||
'invalid-id', {})
|
||||
|
||||
def test_add_item(self):
|
||||
test = {
|
||||
"name": "test",
|
||||
"description": "test description",
|
||||
"kind": "nonrecursive",
|
||||
"abbreviation": "abbr",
|
||||
"rules": []
|
||||
}
|
||||
expected_ret = copy.deepcopy(test)
|
||||
del expected_ret['rules']
|
||||
|
||||
policy_id, policy_obj = self.library_policy_model.add_item(test, {})
|
||||
# self.assertEqual(test['id'], policy_id)
|
||||
test['id'] = policy_id
|
||||
self.assertEqual(test, policy_obj)
|
||||
|
||||
def test_add_item_duplicate_name(self):
|
||||
test = {
|
||||
"name": "test_policy",
|
||||
"description": "test description",
|
||||
"kind": "nonrecursive",
|
||||
"abbreviation": "abbr",
|
||||
"rules": []
|
||||
}
|
||||
self.library_policy_model.add_item(test, {})
|
||||
ret = self.library_policy_model.get_items({})
|
||||
self.assertEqual(len(ret['results']), 3)
|
||||
|
||||
def test_add_item_with_id(self):
|
||||
test = {
|
||||
"name": "test",
|
||||
"description": "test description",
|
||||
"kind": "nonrecursive",
|
||||
"abbreviation": "abbr",
|
||||
"rules": []
|
||||
}
|
||||
|
||||
self.assertRaises(webservice.DataModelException,
|
||||
self.library_policy_model.add_item, test, {}, 'id')
|
||||
|
||||
def test_add_item_without_name(self):
|
||||
test = {
|
||||
"description": "test description",
|
||||
"kind": "nonrecursive",
|
||||
"abbreviation": "abbr"
|
||||
}
|
||||
|
||||
self.assertRaises(webservice.DataModelException,
|
||||
self.library_policy_model.add_item, test, {})
|
||||
|
||||
def test_add_item_with_long_abbreviation(self):
|
||||
test = {
|
||||
"name": "test",
|
||||
"description": "test description",
|
||||
"kind": "nonrecursive",
|
||||
"abbreviation": "123456",
|
||||
"rules": []
|
||||
}
|
||||
self.assertRaises(webservice.DataModelException,
|
||||
self.library_policy_model.add_item, test, {})
|
||||
|
||||
def test_update_item_without_name(self):
|
||||
test = {
|
||||
"description": "test description",
|
||||
"kind": "nonrecursive",
|
||||
"abbreviation": "abbr"
|
||||
}
|
||||
|
||||
self.assertRaises(webservice.DataModelException,
|
||||
self.library_policy_model.update_item,
|
||||
self.policy['id'], test, {})
|
||||
|
||||
def test_update_item_with_long_abbreviation(self):
|
||||
test = {
|
||||
"name": "test",
|
||||
"description": "test description",
|
||||
"kind": "nonrecursive",
|
||||
"abbreviation": "123456",
|
||||
"rules": []
|
||||
}
|
||||
self.assertRaises(webservice.DataModelException,
|
||||
self.library_policy_model.update_item,
|
||||
self.policy['id'], test, {})
|
||||
|
||||
def test_delete_item(self):
|
||||
# delete non-existent policy
|
||||
self.assertRaises(KeyError, self.library_policy_model.delete_item,
|
||||
'no_such_policy', {})
|
||||
|
||||
# delete existing policy
|
||||
expected_ret = self.policy
|
||||
policy_id = self.policy['id']
|
||||
|
||||
ret = self.library_policy_model.delete_item(policy_id, {})
|
||||
self.assertEqual(expected_ret, ret)
|
||||
self.assertRaises(KeyError,
|
||||
self.library_policy_model.get_item,
|
||||
self.policy['id'], {})
|
||||
|
||||
def test_policy_api_model_error(self):
|
||||
"""Test the policy api model."""
|
||||
|
||||
# policy without name
|
||||
self.assertRaises(webservice.DataModelException,
|
||||
self.library_policy_model.add_item,
|
||||
{'rules': []}, {})
|
||||
|
||||
self.assertRaises(webservice.DataModelException,
|
||||
self.library_policy_model.update_item,
|
||||
self.policy['id'], {'rules': []}, {})
|
||||
|
||||
# policy with bad name
|
||||
self.assertRaises(webservice.DataModelException,
|
||||
self.library_policy_model.add_item,
|
||||
{'name': '7*7', 'rules': []}, {})
|
||||
self.assertRaises(webservice.DataModelException,
|
||||
self.library_policy_model.update_item,
|
||||
self.policy['id'], {'name': '7*7', 'rules': []}, {})
|
||||
|
||||
self.assertRaises(webservice.DataModelException,
|
||||
self.library_policy_model.add_item,
|
||||
{'name': 'p(x) :- q(x)'}, {})
|
||||
self.assertRaises(webservice.DataModelException,
|
||||
self.library_policy_model.update_item,
|
||||
self.policy['id'], {'name': 'p(x) :- q(x)'}, {})
|
||||
|
||||
# policy with invalid 'kind'
|
||||
self.assertRaises(webservice.DataModelException,
|
||||
self.library_policy_model.add_item,
|
||||
{'kind': 'nonexistent', 'name': 'alice',
|
||||
'rules': []}, {})
|
||||
self.assertRaises(webservice.DataModelException,
|
||||
self.library_policy_model.update_item,
|
||||
self.policy['id'],
|
||||
{'kind': 'nonexistent', 'name': 'alice',
|
||||
'rules': []}, {})
|
||||
|
||||
def test_update_item(self):
|
||||
replacement_policy = {
|
||||
"name": "new_name",
|
||||
"description": "new test policy2 description",
|
||||
"kind": "nonrecursive",
|
||||
"abbreviation": "newab",
|
||||
"rules": [{"rule": "r(x) :- c(x)", "comment": "test comment",
|
||||
"name": "test name"}]
|
||||
}
|
||||
|
||||
# update non-existent item
|
||||
self.assertRaises(KeyError,
|
||||
self.library_policy_model.update_item, 'no_such_id',
|
||||
replacement_policy, {}, {})
|
||||
|
||||
# update existing item
|
||||
self.library_policy_model.update_item(
|
||||
self.policy2['id'], replacement_policy, {}, {})
|
||||
|
||||
replacement_policy_w_id = copy.deepcopy(replacement_policy)
|
||||
replacement_policy_w_id['id'] = self.policy2['id']
|
||||
|
||||
ret = self.library_policy_model.get_items({})
|
||||
self.assertEqual(len(ret['results']), 2)
|
||||
self.assertTrue(all(p in ret['results']
|
||||
for p in [self.policy,
|
||||
replacement_policy_w_id]))
|
|
@ -0,0 +1,155 @@
|
|||
# Copyright 2017 VMware Inc. All rights reserved.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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 __future__ import print_function
|
||||
from __future__ import division
|
||||
from __future__ import absolute_import
|
||||
|
||||
from congress.db import db_library_policies
|
||||
from congress.tests import base
|
||||
|
||||
|
||||
class TestDbLibraryPolicies(base.SqlTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestDbLibraryPolicies, self).setUp()
|
||||
|
||||
def test_add_policy_no_name(self):
|
||||
self.assertRaises(
|
||||
KeyError, db_library_policies.add_policy, {'rules': []})
|
||||
|
||||
def test_add_policy_no_rules(self):
|
||||
self.assertRaises(KeyError, db_library_policies.add_policy,
|
||||
{'name': 'policy1'})
|
||||
|
||||
def test_add_policy(self):
|
||||
res = db_library_policies.add_policy(
|
||||
{'name': 'policy1', 'abbreviation': 'abbr', 'kind': 'database',
|
||||
'description': 'descrip', 'rules': [{'rule': 'p(x) :- q(x)',
|
||||
'comment': 'test comment',
|
||||
'name': 'testname'}]})
|
||||
self.assertEqual(res.to_dict(include_rules=True),
|
||||
{'id': res['id'],
|
||||
'abbreviation': 'abbr',
|
||||
'kind': 'database',
|
||||
'name': 'policy1',
|
||||
'description': 'descrip',
|
||||
'rules': [{'rule': 'p(x) :- q(x)',
|
||||
'comment': 'test comment',
|
||||
'name': 'testname'}]})
|
||||
|
||||
def test_add_policy_duplicate(self):
|
||||
db_library_policies.add_policy(
|
||||
{'name': 'policy1', 'abbreviation': 'abbr', 'kind': 'database',
|
||||
'description': 'descrip', 'rules': []})
|
||||
self.assertRaises(
|
||||
KeyError, db_library_policies.add_policy,
|
||||
{'name': 'policy1', 'rules': [{'rule': 'p(x) :- q(x)',
|
||||
'comment': 'test comment',
|
||||
'name': 'testname'}]})
|
||||
|
||||
def test_get_policy_empty(self):
|
||||
res = db_library_policies.get_policies()
|
||||
self.assertEqual(res, [])
|
||||
|
||||
self.assertRaises(KeyError, db_library_policies.get_policy,
|
||||
'nosuchpolicy')
|
||||
|
||||
def test_create_get_policy(self):
|
||||
policy1 = db_library_policies.add_policy(
|
||||
{'name': 'policy1', 'abbreviation': 'abbr', 'kind': 'database',
|
||||
'description': 'descrip', 'rules': [{'rule': 'p(x) :- q(x)',
|
||||
'comment': 'test comment',
|
||||
'name': 'testname'}]})
|
||||
res = db_library_policies.get_policies()
|
||||
|
||||
res = db_library_policies.get_policies()
|
||||
self.assertEqual([p.to_dict(include_rules=True) for p in res],
|
||||
[{'id': policy1['id'],
|
||||
'abbreviation': 'abbr',
|
||||
'kind': 'database',
|
||||
'name': 'policy1',
|
||||
'description': 'descrip',
|
||||
'rules': [{'rule': 'p(x) :- q(x)',
|
||||
'comment': 'test comment',
|
||||
'name': 'testname'}]}])
|
||||
|
||||
res = db_library_policies.get_policy(policy1['id'])
|
||||
self.assertEqual(res.to_dict(include_rules=True),
|
||||
{'id': policy1['id'],
|
||||
'abbreviation': 'abbr',
|
||||
'kind': 'database',
|
||||
'name': 'policy1',
|
||||
'description': 'descrip',
|
||||
'rules': [{'rule': 'p(x) :- q(x)',
|
||||
'comment': 'test comment',
|
||||
'name': 'testname'}]})
|
||||
|
||||
self.assertRaises(KeyError, db_library_policies.get_policy,
|
||||
'no_such_policy')
|
||||
|
||||
def test_delete_policy(self):
|
||||
db_library_policies.delete_policy('policy1')
|
||||
|
||||
policy1 = db_library_policies.add_policy(
|
||||
{'name': 'policy1', 'abbreviation': 'abbr', 'kind': 'database',
|
||||
'description': 'descrip', 'rules': [{'rule': 'p(x) :- q(x)',
|
||||
'comment': 'test comment',
|
||||
'name': 'testname'}]})
|
||||
|
||||
policy2 = db_library_policies.add_policy(
|
||||
{'name': 'policy2', 'abbreviation': 'abbr', 'kind': 'database',
|
||||
'description': 'descrip', 'rules': [{'rule': 'p(x) :- q(x)',
|
||||
'comment': 'test comment',
|
||||
'name': 'testname'}]})
|
||||
|
||||
res = db_library_policies.get_policies()
|
||||
self.assertEqual(len(res), 2)
|
||||
|
||||
db_library_policies.delete_policy('no_such_policy')
|
||||
|
||||
res = db_library_policies.get_policies()
|
||||
self.assertEqual(len(res), 2)
|
||||
|
||||
db_library_policies.delete_policy(policy1['id'])
|
||||
|
||||
res = db_library_policies.get_policies()
|
||||
self.assertEqual(len(res), 1)
|
||||
|
||||
db_library_policies.delete_policy(policy2['id'])
|
||||
|
||||
res = db_library_policies.get_policies()
|
||||
self.assertEqual(len(res), 0)
|
||||
|
||||
def test_delete_policies(self):
|
||||
db_library_policies.delete_policies()
|
||||
res = db_library_policies.get_policies()
|
||||
self.assertEqual(len(res), 0)
|
||||
|
||||
db_library_policies.add_policy(
|
||||
{'name': 'policy1', 'abbreviation': 'abbr', 'kind': 'database',
|
||||
'description': 'descrip', 'rules': [{'rule': 'p(x) :- q(x)',
|
||||
'comment': 'test comment',
|
||||
'name': 'testname'}]})
|
||||
|
||||
db_library_policies.add_policy(
|
||||
{'name': 'policy2', 'abbreviation': 'abbr', 'kind': 'database',
|
||||
'description': 'descrip', 'rules': [{'rule': 'p(x) :- q(x)',
|
||||
'comment': 'test comment',
|
||||
'name': 'testname'}]})
|
||||
|
||||
db_library_policies.delete_policies()
|
||||
res = db_library_policies.get_policies()
|
||||
self.assertEqual(len(res), 0)
|
Binary file not shown.
|
@ -0,0 +1,194 @@
|
|||
# Copyright 2017 VMware Inc. All rights reserved.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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 __future__ import print_function
|
||||
from __future__ import division
|
||||
from __future__ import absolute_import
|
||||
|
||||
import copy
|
||||
|
||||
from congress import exception
|
||||
from congress.library_service import library_service
|
||||
from congress.tests import base
|
||||
|
||||
|
||||
class TestLibraryService(base.SqlTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestLibraryService, self).setUp()
|
||||
self.library = library_service.LibraryService('lib-test')
|
||||
|
||||
self.policy1 = {'name': 'policy1', 'abbreviation': 'abbr',
|
||||
'kind': 'database', 'description': 'descrip',
|
||||
'rules': [{'rule': 'p(x) :- q(x)',
|
||||
'comment': 'test comment',
|
||||
'name': 'testname'}]}
|
||||
|
||||
self.policy2 = {'name': 'policy2', 'abbreviation': 'abbr',
|
||||
'kind': 'database', 'description': 'descrip',
|
||||
'rules': [{'rule': 'p(x) :- q(x)',
|
||||
'comment': 'test comment',
|
||||
'name': 'testname'}]}
|
||||
|
||||
self.policy1_meta = copy.deepcopy(self.policy1)
|
||||
self.policy2_meta = copy.deepcopy(self.policy2)
|
||||
del self.policy1_meta['rules']
|
||||
del self.policy2_meta['rules']
|
||||
|
||||
def test_create_policy_no_name(self):
|
||||
self.assertRaises(
|
||||
KeyError, self.library.create_policy, {'rules': []})
|
||||
|
||||
def test_create_policy_no_rules(self):
|
||||
self.assertRaises(KeyError, self.library.create_policy,
|
||||
{'name': 'policy1'})
|
||||
|
||||
def test_create_policy_bad_name(self):
|
||||
self.assertRaises(exception.PolicyException,
|
||||
self.library.create_policy,
|
||||
{'name': 'disallowed-hyphen', 'rules': []})
|
||||
|
||||
def test_create_policy_default(self):
|
||||
res = self.library.create_policy({'name': 'policy1', 'rules': []})
|
||||
self.assertEqual(res, {'id': res['id'], 'abbreviation': 'polic',
|
||||
'kind': 'nonrecursive', 'name': 'policy1',
|
||||
'description': '', 'rules': []})
|
||||
|
||||
def test_create_policy(self):
|
||||
policy_obj = self.library.create_policy(self.policy1)
|
||||
self.policy1['id'] = policy_obj['id']
|
||||
self.assertEqual(policy_obj, self.policy1)
|
||||
|
||||
def test_create_policy_duplicate(self):
|
||||
self.library.create_policy({'name': 'policy1', 'rules': []})
|
||||
self.library.create_policy({'name': 'policy1', 'rules': []})
|
||||
res = self.library.get_policies()
|
||||
self.assertEqual(len(res), 2)
|
||||
|
||||
def test_get_policy_empty(self):
|
||||
res = self.library.get_policies()
|
||||
self.assertEqual(res, [])
|
||||
|
||||
self.assertRaises(KeyError, self.library.get_policy,
|
||||
'nosuchpolicy')
|
||||
|
||||
def test_create_get_policy(self):
|
||||
policy_obj = self.library.create_policy(self.policy1)
|
||||
self.policy1['id'] = policy_obj['id']
|
||||
self.policy1_meta['id'] = policy_obj['id']
|
||||
res = self.library.get_policies()
|
||||
self.assertEqual(res, [self.policy1])
|
||||
|
||||
res = self.library.get_policy(policy_obj['id'])
|
||||
self.assertEqual(res, self.policy1)
|
||||
|
||||
res = self.library.get_policies(include_rules=True)
|
||||
self.assertEqual(res, [self.policy1])
|
||||
|
||||
res = self.library.get_policy(policy_obj['id'], include_rules=False)
|
||||
self.assertEqual(res, self.policy1_meta)
|
||||
|
||||
self.assertRaises(KeyError, self.library.get_policy, 'no_such_policy')
|
||||
|
||||
def test_delete_policy(self):
|
||||
self.assertRaises(KeyError, self.library.delete_policy,
|
||||
'policy1')
|
||||
|
||||
policy_obj = self.library.create_policy(self.policy1)
|
||||
self.policy1['id'] = policy_obj['id']
|
||||
|
||||
policy_obj = self.library.create_policy(self.policy2)
|
||||
self.policy2['id'] = policy_obj['id']
|
||||
|
||||
res = self.library.get_policies()
|
||||
self.assertEqual(len(res), 2)
|
||||
self.assertTrue(all(p in res
|
||||
for p in [self.policy1, self.policy2]))
|
||||
|
||||
self.assertRaises(KeyError, self.library.delete_policy,
|
||||
'no_such_policy')
|
||||
|
||||
res = self.library.delete_policy(self.policy1['id'])
|
||||
self.assertEqual(res, self.policy1)
|
||||
|
||||
res = self.library.get_policies()
|
||||
self.assertEqual(len(res), 1)
|
||||
self.assertEqual(res[0], self.policy2)
|
||||
|
||||
res = self.library.delete_policy(self.policy2['id'])
|
||||
self.assertEqual(res, self.policy2)
|
||||
|
||||
res = self.library.get_policies()
|
||||
self.assertEqual(len(res), 0)
|
||||
|
||||
def test_delete_policies(self):
|
||||
self.library.delete_all_policies()
|
||||
res = self.library.get_policies()
|
||||
self.assertEqual(len(res), 0)
|
||||
|
||||
self.library.create_policy(
|
||||
{'name': 'policy1', 'abbreviation': 'abbr', 'kind': 'database',
|
||||
'description': 'descrip', 'rules': [[{'rule': 'p(x) :- q(x)',
|
||||
'comment': 'test comment',
|
||||
'name': 'testname'}]]})
|
||||
|
||||
self.library.create_policy(
|
||||
{'name': 'policy2', 'abbreviation': 'abbr', 'kind': 'database',
|
||||
'description': 'descrip', 'rules': [[{'rule': 'p(x) :- q(x)',
|
||||
'comment': 'test comment',
|
||||
'name': 'testname'}]]})
|
||||
|
||||
self.library.delete_all_policies()
|
||||
res = self.library.get_policies()
|
||||
self.assertEqual(len(res), 0)
|
||||
|
||||
def test_replace_policy(self):
|
||||
policy1 = self.library.create_policy(
|
||||
{'name': 'policy1', 'abbreviation': 'abbr', 'kind': 'database',
|
||||
'description': 'descrip', 'rules': [[{'rule': 'p(x) :- q(x)',
|
||||
'comment': 'test comment',
|
||||
'name': 'testname'}]]})
|
||||
|
||||
policy2 = self.library.create_policy(
|
||||
{'name': 'policy2', 'abbreviation': 'abbr', 'kind': 'database',
|
||||
'description': 'descrip', 'rules': [[{'rule': 'p(x) :- q(x)',
|
||||
'comment': 'test comment',
|
||||
'name': 'testname'}]]})
|
||||
|
||||
replacement_policy = {
|
||||
"name": "new_name",
|
||||
"description": "new test policy2 description",
|
||||
"kind": "nonrecursive",
|
||||
"abbreviation": "newab",
|
||||
"rules": [{"rule": "r(x) :- c(x)", "comment": "test comment",
|
||||
"name": "test name"}]
|
||||
}
|
||||
|
||||
# update non-existent item
|
||||
self.assertRaises(KeyError,
|
||||
self.library.replace_policy, 'no_such_id',
|
||||
replacement_policy)
|
||||
|
||||
# update existing item
|
||||
self.library.replace_policy(policy2['id'], replacement_policy)
|
||||
|
||||
replacement_policy_w_id = copy.deepcopy(replacement_policy)
|
||||
replacement_policy_w_id['id'] = policy2['id']
|
||||
|
||||
ret = self.library.get_policies()
|
||||
self.assertEqual(len(ret), 2)
|
||||
self.assertTrue(all(p in ret
|
||||
for p in [policy1,
|
||||
replacement_policy_w_id]))
|
|
@ -48,6 +48,7 @@ class BaseTestPolicyCongress(base.SqlTestCase):
|
|||
self.api = self.services['api']
|
||||
self.node = self.services['node']
|
||||
self.engine = self.services['engine']
|
||||
self.library = self.services['library']
|
||||
|
||||
self.neutronv2 = self._create_neutron_mock('neutron')
|
||||
|
||||
|
@ -186,6 +187,12 @@ class TestCongress(BaseTestPolicyCongress):
|
|||
f = lambda: len(neutron.state['ports'])
|
||||
helper.retry_check_function_return_value_not_eq(f, 0)
|
||||
|
||||
def test_library_service(self):
|
||||
# NOTE(ekcs): only the most basic test right now, more detailed testing
|
||||
# done in test_library_service.py
|
||||
res = self.library.get_policies()
|
||||
self.assertEqual(res, [])
|
||||
|
||||
|
||||
class APILocalRouting(BaseTestPolicyCongress):
|
||||
|
||||
|
|
|
@ -31,6 +31,9 @@ class PolicyClient(rest_client.RestClient):
|
|||
policies = '/v1/policies'
|
||||
policies_status = '/v1/policies/%s/status'
|
||||
policy_action = '/v1/policies/%s?%s'
|
||||
library_policy = '/v1/librarypolicies'
|
||||
library_policy_path = '/v1/librarypolicies/%s'
|
||||
library_policies = '/v1/librarypolicies'
|
||||
datasources = '/v1/data-sources'
|
||||
datasource_path = '/v1/data-sources/%s'
|
||||
datasource_tables = '/v1/data-sources/%s/tables'
|
||||
|
@ -63,6 +66,22 @@ class PolicyClient(rest_client.RestClient):
|
|||
self.policy_path % policy)
|
||||
return self._resp_helper(resp, body)
|
||||
|
||||
def create_library_policy(self, body):
|
||||
body = json.dumps(body)
|
||||
resp, body = self.post(
|
||||
self.library_policy, body=body)
|
||||
return self._resp_helper(resp, body)
|
||||
|
||||
def delete_library_policy(self, policy):
|
||||
resp, body = self.delete(
|
||||
self.library_policy_path % policy)
|
||||
return self._resp_helper(resp, body)
|
||||
|
||||
def show_library_policy(self, policy):
|
||||
resp, body = self.get(
|
||||
self.library_policy_path % policy)
|
||||
return self._resp_helper(resp, body)
|
||||
|
||||
def create_policy_rule(self, policy_name, body=None):
|
||||
body = json.dumps(body)
|
||||
resp, body = self.post(
|
||||
|
@ -95,6 +114,10 @@ class PolicyClient(rest_client.RestClient):
|
|||
resp, body = self.get(self.policies)
|
||||
return self._resp_helper(resp, body)
|
||||
|
||||
def list_library_policy(self):
|
||||
resp, body = self.get(self.library_policies)
|
||||
return self._resp_helper(resp, body)
|
||||
|
||||
def list_policy_tables(self, policy_name):
|
||||
resp, body = self.get(self.policy_tables % (policy_name))
|
||||
return self._resp_helper(resp, body)
|
||||
|
|
|
@ -190,6 +190,54 @@ class TestPolicyBasicOps(manager_congress.ScenarioPolicyBase):
|
|||
self.assertEqual(f(), meta_data)
|
||||
|
||||
|
||||
class TestPolicyLibraryBasicOps(manager_congress.ScenarioPolicyBase):
|
||||
@decorators.attr(type='smoke')
|
||||
def test_policy_library_basic_op(self):
|
||||
response = self.admin_manager.congress_client.list_library_policy()
|
||||
initial_state = response['results']
|
||||
|
||||
test_policy = {
|
||||
"name": "test_policy",
|
||||
"description": "test policy description",
|
||||
"kind": "nonrecursive",
|
||||
"abbreviation": "abbr",
|
||||
"rules": [{"rule": "p(x) :- q(x)", "comment": "test comment",
|
||||
"name": "test name"},
|
||||
{"rule": "p(x) :- q2(x)", "comment": "test comment2",
|
||||
"name": "test name2"}]
|
||||
}
|
||||
response = self.admin_manager.congress_client.create_library_policy(
|
||||
test_policy)
|
||||
policy_id = response['id']
|
||||
test_policy['id'] = policy_id
|
||||
|
||||
def delete_if_found(id_):
|
||||
try:
|
||||
self.admin_manager.congress_client.delete_library_policy(id_)
|
||||
except exceptions.NotFound:
|
||||
pass
|
||||
|
||||
self.addCleanup(delete_if_found, policy_id)
|
||||
|
||||
response = self.admin_manager.congress_client.list_library_policy()
|
||||
new_state = response['results']
|
||||
|
||||
self.assertEqual(len(initial_state) + 1, len(new_state),
|
||||
'new library policy not reflected in list results')
|
||||
self.assertIn(test_policy, new_state,
|
||||
'new library policy not reflected in list results')
|
||||
|
||||
self.admin_manager.congress_client.delete_library_policy(policy_id)
|
||||
|
||||
response = self.admin_manager.congress_client.list_library_policy()
|
||||
new_state = response['results']
|
||||
|
||||
self.assertEqual(len(initial_state), len(new_state),
|
||||
'library policy delete not reflected in list results')
|
||||
self.assertNotIn(test_policy, new_state,
|
||||
'library policy delete not reflected in list results')
|
||||
|
||||
|
||||
class TestCongressDataSources(manager_congress.ScenarioPolicyBase):
|
||||
|
||||
@classmethod
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
prelude: >
|
||||
upgrade:
|
||||
- A new database table `librarypolicies` is added;
|
||||
alembic migration scripts included.
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
Babel!=2.4.0,>=2.3.4 # BSD
|
||||
eventlet!=0.18.3,!=0.20.1,<0.21.0,>=0.18.2 # MIT
|
||||
jsonschema>=2.0.0,<3.0.0,!=2.5.0 # MIT
|
||||
PuLP>=1.4.1 # MIT
|
||||
keystoneauth1>=2.21.0 # Apache-2.0
|
||||
keystonemiddleware>=4.12.0 # Apache-2.0
|
||||
|
|
Loading…
Reference in New Issue