diff options
author | Ruan HE <ruan.he@orange.com> | 2015-09-07 12:34:45 +0000 |
---|---|---|
committer | Gerrit Code Review <gerrit@172.30.200.206> | 2015-09-07 12:34:45 +0000 |
commit | 312b1702ee332a2b8d71d88f849b55d8242fe45b (patch) | |
tree | 8ad8a1034815dfd3aacac61204bdf4eb13d2c132 /keystone-moon/keystone/contrib | |
parent | 096f4359ce40e68e89be0c569ca78c0a8aaac65f (diff) | |
parent | fe16e5727e3efc8c096e79bcb181d04a4ac8416c (diff) |
Merge "End of Keystone code update to branch Master and fix some bugs."
Diffstat (limited to 'keystone-moon/keystone/contrib')
8 files changed, 151 insertions, 104 deletions
diff --git a/keystone-moon/keystone/contrib/moon/backends/memory.py b/keystone-moon/keystone/contrib/moon/backends/memory.py index 7a996847..1d5d5fcf 100644 --- a/keystone-moon/keystone/contrib/moon/backends/memory.py +++ b/keystone-moon/keystone/contrib/moon/backends/memory.py @@ -9,9 +9,11 @@ import os import json from keystone import config from keystone.contrib.moon.core import ConfigurationDriver +from oslo_log import log CONF = config.CONF +LOG = log.getLogger(__name__) class ConfigurationConnector(ConfigurationDriver): @@ -36,8 +38,12 @@ class ConfigurationConnector(ConfigurationDriver): nodes = glob(os.path.join(CONF.moon.policy_directory, "*")) templates = dict() for node in nodes: + try: + metadata = json.load(open(os.path.join(node, "metadata.json"))) + except IOError: + # Note (asteroide): it's not a true policy directory, so we forgive it + continue templates[os.path.basename(node)] = dict() - metadata = json.load(open(os.path.join(node, "metadata.json"))) templates[os.path.basename(node)]["name"] = metadata["name"] templates[os.path.basename(node)]["description"] = metadata["description"] return templates diff --git a/keystone-moon/keystone/contrib/moon/backends/sql.py b/keystone-moon/keystone/contrib/moon/backends/sql.py index cb64c1f7..7cbbd4c0 100644 --- a/keystone-moon/keystone/contrib/moon/backends/sql.py +++ b/keystone-moon/keystone/contrib/moon/backends/sql.py @@ -429,12 +429,13 @@ class IntraExtensionConnector(IntraExtensionDriver): ) if not ref: session.add(new_ref) + ref = new_ref else: for attr in SubjectCategory.attributes: if attr != 'id': setattr(ref, attr, getattr(new_ref, attr)) session.flush() - return {subject_category_id: self.get_subject_categories_dict(intra_extension_id)[subject_category_id]} + return {subject_category_id: SubjectCategory.to_dict(ref)['subject_category']} def del_subject_category(self, intra_extension_id, subject_category_id): with sql.transaction() as session: @@ -466,12 +467,13 @@ class IntraExtensionConnector(IntraExtensionDriver): ) if not ref: session.add(new_ref) + ref = new_ref else: for attr in ObjectCategory.attributes: if attr != 'id': setattr(ref, attr, getattr(new_ref, attr)) session.flush() - return {object_category_id: self.get_object_categories_dict(intra_extension_id)[object_category_id]} + return {object_category_id: ObjectCategory.to_dict(ref)['object_category']} def del_object_category(self, intra_extension_id, object_category_id): with sql.transaction() as session: @@ -503,12 +505,13 @@ class IntraExtensionConnector(IntraExtensionDriver): ) if not ref: session.add(new_ref) + ref = new_ref else: for attr in ActionCategory.attributes: if attr != 'id': setattr(ref, attr, getattr(new_ref, attr)) session.flush() - return {action_category_id: self.get_action_categories_dict(intra_extension_id)[action_category_id]} + return {action_category_id: ActionCategory.to_dict(ref)['action_category']} def del_action_category(self, intra_extension_id, action_category_id): with sql.transaction() as session: @@ -542,12 +545,13 @@ class IntraExtensionConnector(IntraExtensionDriver): ) if not ref: session.add(new_ref) + ref = new_ref else: for attr in Subject.attributes: if attr != 'id': setattr(ref, attr, getattr(new_ref, attr)) session.flush() - return {subject_id: self.get_subjects_dict(intra_extension_id)[subject_id]} + return {subject_id: Subject.to_dict(ref)['subject']} def del_subject(self, intra_extension_id, subject_id): with sql.transaction() as session: @@ -577,12 +581,13 @@ class IntraExtensionConnector(IntraExtensionDriver): ) if not ref: session.add(new_ref) + ref = new_ref else: for attr in Object.attributes: if attr != 'id': setattr(ref, attr, getattr(new_ref, attr)) session.flush() - return {object_id: self.get_objects_dict(intra_extension_id)[object_id]} + return {object_id: Object.to_dict(ref)['object']} def del_object(self, intra_extension_id, object_id): with sql.transaction() as session: @@ -612,12 +617,13 @@ class IntraExtensionConnector(IntraExtensionDriver): ) if not ref: session.add(new_ref) + ref = new_ref else: for attr in Action.attributes: if attr != 'id': setattr(ref, attr, getattr(new_ref, attr)) session.flush() - return {action_id: self.get_actions_dict(intra_extension_id)[action_id]} + return {action_id: Action.to_dict(ref)['action']} def del_action(self, intra_extension_id, action_id): with sql.transaction() as session: @@ -650,12 +656,13 @@ class IntraExtensionConnector(IntraExtensionDriver): ) if not ref: session.add(new_ref) + ref = new_ref else: for attr in Subject.attributes: if attr != 'id': setattr(ref, attr, getattr(new_ref, attr)) session.flush() - return {subject_scope_id: self.get_subject_scopes_dict(intra_extension_id, subject_category_id)[subject_scope_id]} + return {subject_scope_id: SubjectScope.to_dict(ref)['subject_scope']} def del_subject_scope(self, intra_extension_id, subject_category_id, subject_scope_id): with sql.transaction() as session: @@ -688,12 +695,13 @@ class IntraExtensionConnector(IntraExtensionDriver): ) if not ref: session.add(new_ref) + ref = new_ref else: for attr in Object.attributes: if attr != 'id': setattr(ref, attr, getattr(new_ref, attr)) session.flush() - return {object_scope_id: self.get_object_scopes_dict(intra_extension_id, object_category_id)[object_scope_id]} + return {object_scope_id: ObjectScope.to_dict(ref)['object_scope']} def del_object_scope(self, intra_extension_id, object_category_id, object_scope_id): with sql.transaction() as session: @@ -726,12 +734,13 @@ class IntraExtensionConnector(IntraExtensionDriver): ) if not ref: session.add(new_ref) + ref = new_ref else: for attr in Action.attributes: if attr != 'id': setattr(ref, attr, getattr(new_ref, attr)) session.flush() - return {action_scope_id: self.get_action_scopes_dict(intra_extension_id, action_category_id)[action_scope_id]} + return {action_scope_id: ActionScope.to_dict(ref)['action_scope']} def del_action_scope(self, intra_extension_id, action_category_id, action_scope_id): with sql.transaction() as session: @@ -767,6 +776,7 @@ class IntraExtensionConnector(IntraExtensionDriver): ) if not ref: session.add(new_ref) + ref = new_ref else: for attr in SubjectAssignment.attributes: if attr != 'id': diff --git a/keystone-moon/keystone/contrib/moon/controllers.py b/keystone-moon/keystone/contrib/moon/controllers.py index 8a950e7a..4065eabf 100644 --- a/keystone-moon/keystone/contrib/moon/controllers.py +++ b/keystone-moon/keystone/contrib/moon/controllers.py @@ -14,7 +14,7 @@ CONF = config.CONF LOG = log.getLogger(__name__) -@dependency.requires('configuration_api') +@dependency.requires('configuration_api', 'root_api', 'moonlog_api') class Configuration(controller.V3Controller): collection_name = 'configurations' member_name = 'configuration' @@ -63,14 +63,14 @@ class Tenants(controller.V3Controller): def add_tenant(self, context, **kw): user_id = self._get_user_id_from_token(context.get('token_id')) # Next line will raise an error if tenant doesn't exist - k_tenant_dict = self.resource_api.get_project_by_name(kw.get('tenant_name', None)) + k_tenant_dict = self.resource_api.get_project_by_name(kw.get('tenant_name'), "default") tenant_dict = dict() tenant_dict['id'] = k_tenant_dict['id'] tenant_dict['name'] = kw.get('tenant_name', None) tenant_dict['description'] = kw.get('tenant_description', None) tenant_dict['intra_authz_extension_id'] = kw.get('tenant_intra_authz_extension_id', None) tenant_dict['intra_admin_extension_id'] = kw.get('tenant_intra_admin_extension_id', None) - return self.tenant_api.add_tenant_dict(user_id, tenant_dict) + return self.tenant_api.add_tenant_dict(user_id, tenant_dict['id'], tenant_dict) @controller.protected() def get_tenant(self, context, **kw): diff --git a/keystone-moon/keystone/contrib/moon/core.py b/keystone-moon/keystone/contrib/moon/core.py index 4a68cdaa..cf15b6ff 100644 --- a/keystone-moon/keystone/contrib/moon/core.py +++ b/keystone-moon/keystone/contrib/moon/core.py @@ -19,44 +19,56 @@ from keystone.common import dependency from keystone import exception from oslo_config import cfg from keystone.i18n import _ +from keystone.common import extension from keystone.contrib.moon.exception import * from keystone.contrib.moon.algorithms import * -CONF = config.CONF +CONF = cfg.CONF LOG = log.getLogger(__name__) -# ADMIN_ID = None # default user_id for internal invocation -# ROOT_EXTENSION_ID = None -# ROOT_EXTENSION_MODEL = "policy_root" - - -_OPTS = [ - cfg.StrOpt('configuration_driver', - default='keystone.contrib.moon.backends.memory.ConfigurationConnector', - help='Configuration backend driver.'), - cfg.StrOpt('tenant_driver', - default='keystone.contrib.moon.backends.sql.TenantConnector', - help='Tenant backend driver.'), - cfg.StrOpt('authz_driver', - default='keystone.contrib.moon.backends.flat.SuperExtensionConnector', - help='Authorisation backend driver.'), - cfg.StrOpt('intraextension_driver', - default='keystone.contrib.moon.backends.sql.IntraExtensionConnector', - help='IntraExtension backend driver.'), - cfg.StrOpt('interextension_driver', - default='keystone.contrib.moon.backends.sql.InterExtensionConnector', - help='InterExtension backend driver.'), - cfg.StrOpt('log_driver', - default='keystone.contrib.moon.backends.flat.LogConnector', - help='Logs backend driver.'), - cfg.StrOpt('policy_directory', - default='/etc/keystone/policies', - help='Local directory where all policies are stored.'), - cfg.StrOpt('root_policy_directory', - default='policy_root', - help='Local directory where Root IntraExtension configuration is stored.'), -] -CONF.register_opts(_OPTS, group='moon') + +EXTENSION_DATA = { + 'name': 'OpenStack Moon APIs', + 'namespace': 'http://docs.openstack.org/identity/api/ext/' + 'OS-MOON', + 'alias': 'OS-MOON', + 'updated': '2015-09-02T12:00:0-00:00', + 'description': 'OpenStack Authorization Providers Mechanism.', + 'links': [{ + 'rel': 'describedby', + 'type': 'text/html', + 'href': 'https://git.opnfv.org/moon.git' + }]} +extension.register_admin_extension(EXTENSION_DATA['alias'], EXTENSION_DATA) +extension.register_public_extension(EXTENSION_DATA['alias'], EXTENSION_DATA) + +# _OPTS = [ +# cfg.StrOpt('configuration_driver', +# default='keystone.contrib.moon.backends.memory.ConfigurationConnector', +# help='Configuration backend driver.'), +# cfg.StrOpt('tenant_driver', +# default='keystone.contrib.moon.backends.sql.TenantConnector', +# help='Tenant backend driver.'), +# cfg.StrOpt('authz_driver', +# default='keystone.contrib.moon.backends.flat.SuperExtensionConnector', +# help='Authorisation backend driver.'), +# cfg.StrOpt('intraextension_driver', +# default='keystone.contrib.moon.backends.sql.IntraExtensionConnector', +# help='IntraExtension backend driver.'), +# cfg.StrOpt('interextension_driver', +# default='keystone.contrib.moon.backends.sql.InterExtensionConnector', +# help='InterExtension backend driver.'), +# cfg.StrOpt('log_driver', +# default='keystone.contrib.moon.backends.flat.LogConnector', +# help='Logs backend driver.'), +# cfg.StrOpt('policy_directory', +# default='/etc/keystone/policies', +# help='Local directory where all policies are stored.'), +# cfg.StrOpt('root_policy_directory', +# default='policy_root', +# help='Local directory where Root IntraExtension configuration is stored.'), +# ] +# CONF.register_opts(_OPTS, group='moon') def filter_input(func_or_str): @@ -177,8 +189,20 @@ def enforce(action_names, object_name, **extra): else: intra_extensions_dict = self.admin_api.driver.get_intra_extensions_dict() if intra_extension_id not in intra_extensions_dict: - raise IntraExtensionUnknown() - tenants_dict = self.tenant_api.driver.get_tenants_dict() + # if id is not an intra_extension, maybe it is a tenant id + try: + tenants_dict = self.tenant_api.driver.get_tenants_dict() + except AttributeError: + tenants_dict = self.driver.get_tenants_dict() + if intra_extension_id in tenants_dict: + # id is in fact a tenant id so, we must check against the Root intra_extension + intra_extension_id = intra_root_extension_id + else: + raise IntraExtensionUnknown() + try: + tenants_dict = self.tenant_api.driver.get_tenants_dict() + except AttributeError: + tenants_dict = self.driver.get_tenants_dict() for _tenant_id in tenants_dict: if tenants_dict[_tenant_id]['intra_authz_extension_id'] == intra_extension_id or \ tenants_dict[_tenant_id]['intra_admin_extension_id'] == intra_extension_id: @@ -374,7 +398,7 @@ class TenantManager(manager.Manager): @filter_input @enforce(("read", "write"), "tenants") - def add_tenant_dict(self, user_id, tenant_dict): + def add_tenant_dict(self, user_id, tenant_id, tenant_dict): tenants_dict = self.driver.get_tenants_dict() for tenant_id in tenants_dict: if tenants_dict[tenant_id]['name'] == tenant_dict['name']: @@ -459,8 +483,7 @@ class IntraExtensionManager(manager.Manager): driver_namespace = 'keystone.moon.intraextension' def __init__(self): - driver = CONF.moon.intraextension_driver - super(IntraExtensionManager, self).__init__(driver) + super(IntraExtensionManager, self).__init__(CONF.moon.intraextension_driver) def __get_authz_buffer(self, intra_extension_id, subject_id, object_id, action_id): """ @@ -857,7 +880,13 @@ class IntraExtensionManager(manager.Manager): ie_dict["genre"] = "admin" ie_dict["description"] = "policy_root" ref = self.driver.set_intra_extension_dict(ie_dict['id'], ie_dict) - self.moonlog_api.debug("Creation of IE: {}".format(ref)) + try: + self.moonlog_api.debug("Creation of IE: {}".format(ref)) + except AttributeError: + pass + # Creation of the root intra extension raise an error here because + # self.moonlog_api doesn't exist. + # FIXME (asteroide): understand why moonlog_api raise an error here... # read the template given by "model" and populate default variables template_dir = os.path.join(CONF.moon.policy_directory, ie_dict["model"]) self.__load_metadata_file(ie_dict, template_dir) @@ -2038,11 +2067,16 @@ class IntraExtensionRootManager(IntraExtensionManager): def __init__(self): super(IntraExtensionRootManager, self).__init__() extensions = self.admin_api.driver.get_intra_extensions_dict() + LOG.debug("extensions {}".format(extensions)) for extension_id, extension_dict in extensions.iteritems(): - if extension_dict["model"] == CONF.moon.root_policy_directory: + LOG.debug("{} / {}".format(extension_dict["name"], CONF.moon.root_policy_directory)) + if extension_dict["name"] == CONF.moon.root_policy_directory: self.root_extension_id = extension_id + break else: extension = self.admin_api.load_root_intra_extension_dict(CONF.moon.root_policy_directory) + if not extension: + raise IntraExtensionCreationError("The root extension is not created.") self.root_extension_id = extension['id'] self.root_admin_id = self.__compute_admin_id_for_root_extension() @@ -2054,7 +2088,9 @@ class IntraExtensionRootManager(IntraExtensionManager): return {self.root_extension_id: self.admin_api.driver.get_intra_extensions_dict()[self.root_extension_id]} def __compute_admin_id_for_root_extension(self): + LOG.debug(self.admin_api.driver.get_subjects_dict(self.root_extension_id)) for subject_id, subject_dict in self.admin_api.driver.get_subjects_dict(self.root_extension_id).iteritems(): + LOG.debug("subject_name = {}".format(subject_dict["name"])) if subject_dict["name"] == "admin": return subject_id raise RootExtensionNotInitialized() diff --git a/keystone-moon/keystone/contrib/moon/migrate_repo/versions/001_moon.py b/keystone-moon/keystone/contrib/moon/migrate_repo/versions/001_moon.py index 4fd26bef..737619cb 100644 --- a/keystone-moon/keystone/contrib/moon/migrate_repo/versions/001_moon.py +++ b/keystone-moon/keystone/contrib/moon/migrate_repo/versions/001_moon.py @@ -3,7 +3,6 @@ # license which can be found in the file 'LICENSE' in this package distribution # or at 'http://www.apache.org/licenses/LICENSE-2.0'. -from uuid import uuid4 import sqlalchemy as sql from keystone.common import sql as k_sql @@ -21,12 +20,6 @@ def upgrade(migrate_engine): mysql_charset='utf8') intra_extension_table.create(migrate_engine, checkfirst=True) - # intra_extension_table.insert().values(id=uuid4().hex, intra_extension={ - # 'name': "Root Extension", - # 'description': "The root intra extension", - # 'model': 'admin' - # }) - tenant_table = sql.Table( 'tenants', meta, @@ -119,7 +112,7 @@ def upgrade(migrate_engine): object_scopes_table.create(migrate_engine, checkfirst=True) action_scopes_table = sql.Table( - 'action_category_scopes', + 'action_scopes', meta, sql.Column('id', sql.String(64), primary_key=True), sql.Column('action_scope', k_sql.JsonBlob(), nullable=True), @@ -133,7 +126,7 @@ def upgrade(migrate_engine): 'subject_assignments', meta, sql.Column('id', sql.String(64), primary_key=True), - sql.Column('subject_category_assignment', k_sql.JsonBlob(), nullable=True), + sql.Column('subject_assignment', k_sql.JsonBlob(), nullable=True), sql.Column('intra_extension_id', sql.ForeignKey("intra_extensions.id"), nullable=False), sql.Column('subject_id', sql.ForeignKey("subjects.id"), nullable=False), sql.Column('subject_category_id', sql.ForeignKey("subject_categories.id"), nullable=False), @@ -203,12 +196,12 @@ def downgrade(migrate_engine): for _table in ( 'rules', 'sub_meta_rules', - 'action_category_assignments', - 'object_category_assignments', - 'subject_category_assignments', - 'action_category_scopes', - 'object_category_scopes', - 'subject_category_scopes', + 'action_assignments', + 'object_assignments', + 'subject_assignments', + 'action_scopes', + 'object_scopes', + 'subject_scopes', 'actions', 'objects', 'subjects', diff --git a/keystone-moon/keystone/contrib/moon/migrate_repo/versions/002_moon.py b/keystone-moon/keystone/contrib/moon/migrate_repo/versions/002_moon.py index a0f9095f..14e22fc4 100644 --- a/keystone-moon/keystone/contrib/moon/migrate_repo/versions/002_moon.py +++ b/keystone-moon/keystone/contrib/moon/migrate_repo/versions/002_moon.py @@ -11,24 +11,24 @@ def upgrade(migrate_engine): meta = sql.MetaData() meta.bind = migrate_engine - region_table = sql.Table( - 'inter_extension', - meta, - sql.Column('id', sql.String(64), primary_key=True), - sql.Column('requesting_intra_extension_uuid', sql.String(64), nullable=False), - sql.Column('requested_intra_extension_uuid', sql.String(64), nullable=False), - sql.Column('virtual_entity_uuid', sql.String(64), nullable=False), - sql.Column('genre', sql.String(64), nullable=False), - sql.Column('description', sql.Text(), nullable=True), - - mysql_engine='InnoDB', - mysql_charset='utf8') - region_table.create(migrate_engine, checkfirst=True) - - +# region_table = sql.Table( +# 'inter_extension', +# meta, +# sql.Column('id', sql.String(64), primary_key=True), +# sql.Column('requesting_intra_extension_uuid', sql.String(64), nullable=False), +# sql.Column('requested_intra_extension_uuid', sql.String(64), nullable=False), +# sql.Column('virtual_entity_uuid', sql.String(64), nullable=False), +# sql.Column('genre', sql.String(64), nullable=False), +# sql.Column('description', sql.Text(), nullable=True), +# +# mysql_engine='InnoDB', +# mysql_charset='utf8') +# region_table.create(migrate_engine, checkfirst=True) +# +# def downgrade(migrate_engine): meta = sql.MetaData() meta.bind = migrate_engine - table = sql.Table('inter_extension', meta, autoload=True) - table.drop(migrate_engine, checkfirst=True) +# table = sql.Table('inter_extension', meta, autoload=True) +# table.drop(migrate_engine, checkfirst=True) diff --git a/keystone-moon/keystone/contrib/moon/migrate_repo/versions/003_moon.py b/keystone-moon/keystone/contrib/moon/migrate_repo/versions/003_moon.py index 06932754..f11fb2fb 100644 --- a/keystone-moon/keystone/contrib/moon/migrate_repo/versions/003_moon.py +++ b/keystone-moon/keystone/contrib/moon/migrate_repo/versions/003_moon.py @@ -11,22 +11,22 @@ def upgrade(migrate_engine): meta = sql.MetaData() meta.bind = migrate_engine - region_table = sql.Table( - 'tenants', - meta, - sql.Column('id', sql.String(64), primary_key=True), - sql.Column('name', sql.String(128), nullable=True), - sql.Column('authz', sql.String(64), nullable=True), - sql.Column('admin', sql.String(64), nullable=True), - - mysql_engine='InnoDB', - mysql_charset='utf8') - region_table.create(migrate_engine, checkfirst=True) - +# region_table = sql.Table( +# 'tenants', +# meta, +# sql.Column('id', sql.String(64), primary_key=True), +# sql.Column('name', sql.String(128), nullable=True), +# sql.Column('authz', sql.String(64), nullable=True), +# sql.Column('admin', sql.String(64), nullable=True), +# +# mysql_engine='InnoDB', +# mysql_charset='utf8') +# region_table.create(migrate_engine, checkfirst=True) +# def downgrade(migrate_engine): meta = sql.MetaData() meta.bind = migrate_engine - - table = sql.Table('tenants', meta, autoload=True) - table.drop(migrate_engine, checkfirst=True) +# +# table = sql.Table('tenants', meta, autoload=True) +# table.drop(migrate_engine, checkfirst=True) diff --git a/keystone-moon/keystone/contrib/moon/routers.py b/keystone-moon/keystone/contrib/moon/routers.py index 63915092..340bd194 100644 --- a/keystone-moon/keystone/contrib/moon/routers.py +++ b/keystone-moon/keystone/contrib/moon/routers.py @@ -7,6 +7,9 @@ from keystone.contrib.moon import controllers from keystone.common import wsgi +from oslo_log import log + +LOG = log.getLogger(__name__) class Routers(wsgi.V3ExtensionRouter): @@ -23,7 +26,7 @@ class Routers(wsgi.V3ExtensionRouter): def _get_path(component): return 'http://docs.openstack.org/api/openstack-authz/3/param/{}'.format(component) - def append_v3_routers(self, mapper, routers): + def add_routes(self, mapper): # Controllers creation authz_controller = controllers.Authz_v3() configuration_controller = controllers.Configuration() @@ -31,12 +34,11 @@ class Routers(wsgi.V3ExtensionRouter): tenants_controller = controllers.Tenants() logs_controller = controllers.Logs() inter_ext_controller = controllers.InterExtensions() - # Configuration route self._add_resource( mapper, configuration_controller, path=self.PATH_PREFIX+'/configuration/templates', - get_action='get_templates', + get_action='get_policy_templates', rel=self._get_rel('templates'), path_vars={}) self._add_resource( @@ -47,7 +49,7 @@ class Routers(wsgi.V3ExtensionRouter): path_vars={}) self._add_resource( mapper, configuration_controller, - path=self.PATH_PREFIX+'/configuration/sub_meta_rule_relations', + path=self.PATH_PREFIX+'/configuration/sub_meta_rule_algorithms', get_action='get_sub_meta_rule_algorithms', rel=self._get_rel('sub_meta_rule_algorithms'), path_vars={}) |