- Grouper daemon log
- Grouper Loader add new job
- Grouper Loader classlist example from Penn
- Grouper loader configure via web service
- Grouper Loader example include exclude and privileges
- Grouper loader example with privileges
- Grouper loader failsafe
- Grouper - Loader for attribute or permission definitions
- Grouper loader from attributes example
- Grouper - Loader LDAP
- Grouper loader on UI
- Grouper Loader permissions or attributes POC with GSH
- Grouper loader real time updates
- Grouper loader SQL group list example
- Grouper loader SQL simple example
- Grouper loader with CSV data sources
- Grouper selective loader from attributes example
- Organization hierarchies via the grouper loader
The Grouper Loader allows you to automatically manage Grouper memberships based on a data source. See architectural diagram.
To find out if Grouper Loader jobs are executing successfully, use Grouper Diagnostics.
Here is an example of Grouper Loader being used to automatically manage Grouper memberships based on a data source.
You should run the grouper loader all the time for any Grouper deployment. It also runs background tasks even if you aren't automatically loading groups.
Penn is using it in production to load membership for groups, and for groups of groups (in Penn's case, org lists).
Ad hoc groups with the Grouper Loader
Loader manages simple group or a list of groups in one job
One-time setup in your Grouper database
To make a dynamic (loadable) group, first you need the correct metadata in Grouper. The easiest way is to set the grouper-loader.properties key loader.autoadd.typesAttributes to true. If you don't want to do that, then here is the setup in GSH:
Note that a loadable group has the type "grouperLoader", and there are some attributes that you can set about the group:
- grouperLoaderType: there will be various types to choose from, currently only these are available :
- SQL_SIMPLE is a group whose membership is fed from a query, and the whole query's results will be the groups results (not incremental).
- SQL_GROUP_LIST requires a group_name column in query, so one query can control multiple group memberships. This will default to SQL_SIMPLE if no group_name before the FROM in the query. Else it will be SQL_GROUP_LIST.
- grouperLoaderDbName: if it is a sql based type, this is the name in the grouper-loader.properties of the db connection properties. If this is set to the reserve value "grouper" it will use the grouper db (in grouper.hibernate.properties). Here is a snippet from grouper-loader.properties where grouperLoaderDbName=warehouse :
- grouperLoaderScheduleType: Grouper-loader uses the quartz open source job scheduler, and currently supports two schedule types:
- CRON: This is a cron-like syntax that I think is quartz specific
- START_TO_START_INTERVAL: This is a repeated schedule that runs based on a delay from the start of one run to the start of another run
This defaults to CRON if there is a value for grouperLoaderQuartzCron (see below), else it defaults to START_TO_START_INTERVAL. Note that a job will not start if a previous run has not finished.
- grouperLoaderQuery: This is the query to run in the DB, which must have certain columns required or optional based on the grouperLoaderType. e.g. for SQL_SIMPLE, the SUBJECT_ID is required, and the SUBJECT_SOURCE_ID is optional. If your DB supports views, might not be a bad idea to link query up to a view so you can easily see what it will return and change it without affecting the group attribute. But will work with any select query. This is required. Note: in Grouper v2.1 you can try having SUBJECT_IDENTIFIER or SUBJECT_ID_OR_IDENTIFIER instead of SUBJECT_ID, though each is less efficient than the next since they require an extra subject lookup or multiple subject lookups per row. SQL_GROUP_LIST requires a group_name column in query,
- grouperLoaderQuartzCron: If a CRON schedule type, this is the cron setting string from the quartz product to run a job daily, hourly, weekly, etc: http://www.opensymphony.com/quartz/wikidocs/TutorialLesson6.html
- grouperLoaderIntervalSeconds: If a START_TO_START_INTERVAL schedule type, this is the number of seconds between the start of one run to the start of another run. This defaults to daily if not filled in. Note, for daily jobs, it is probably better to use cron so it won't fire up each time the loader is restarted.
- grouperLoaderPriority: Quartz has a fixed threadpool (max configured in the grouper-loader.properties), and when the max is reached, then jobs are prioritized by this integer. The higher the better, and the default if not set is 5.
- grouperLoaderAndGroups: If you want to restrict membership in the dynamic group based on other group(s), put the list of group names here comma-separated. The require groups means if you put a group names in there (e.g. school:community:employee) then it will "and" that group with the member list from the loader. So only members of the group from the loader query who are also employees will be in the resulting group
- grouperLoaderGroupTypes: whatever you put in the value should be comma separated GroupTypes which will be applied to the loaded groups. The reason this enhancement exists is so we can do a SQL_GROUP_LIST query and attach addIncludeExclude to the groups. Note, if you do this (or use some requireGroups), the group name in the loader query should end in the system of record suffix, which by default is _systemOfRecord.
- To use the "addIncludeExclude" GroupType you need to set
grouperIncludeExclude.use = truein grouper.properties
- To use the "addIncludeExclude" GroupType you need to set
- grouperLoaderGroupsLike: this should be a sql like string (e.g. school:orgs:%org%_systemOfRecord), and the loader should be able to query group names to see which names are managed by this loader job. So if a group falls off the loader resultset (or is moved), this will help the loader remove the members from this group. Note, if the group is used anywhere as a member or composite member, it won't be removed. All include/exclude/requireGroups will be removed. Though the two groups, include and exclude, will not be removed if they have members. There is a grouper-loader.properties setting to note remove loader groups if empty and not used:
- grouperLoaderGroupQuery: query (optional) for SQL_GROUP_LIST which should return cols: group_name (full path of the group), group_display_name (optional, will be used for group display name), group_description (optional, will default to "<extension> auto-created by grouperLoader" unless loader.allowBlankGroupDescriptions is set to true in grouper-loader.properties). Note: the parent stem display names are only changed when creating them. This should return all groups in the membership list, and if not there, its ok, the extension will be used as display extension, and a generated description. Note: the display name is the display path, with the display extension of each parent stem. If there is a column named any of the following: readers, viewers, admins, updaters, optins, optouts, group_attr_readers, group_attr_updaters, then the data in the column (comma separated subjectId's or subjectIdentifers (which can include group names) will be assigned to that privilege list. Note, existing assignments to that privilege list will not be removed, so if you remove an item from the query, you will need to manually remove it from the groups. This is a way to have a loaderJob-wide list of readers or viewers.
grouperLoaderDisplayNameSyncType: (optional attribute) - (v2.5.53+) This attribute allows syncing folder display extensions between source and grouper. This attribute is for SQL_GROUP_LIST and can only be set when grouperLoaderGroupQuery is set. Pick which type of display name sync. By default Grouper will assign the display name of a folder but then not change it in future. Grouper will by default change group display extensions as they change in the loader group query.
grouperLoaderDisplayNameSyncBaseFolderName: (v2.5.53+) Base folder system name after which you would like to sync folders display extensions between source and grouper. Specify (e.g a:b:c) after which you want the loader to sync display names as well. Also, if you specify 'root' as the base folder name, then all the folder extensions will sync. This is a good option if all your groups are in a certain base folder and you want all folder display extensions under that to sync.
grouperLoaderDisplayNameSyncLevels: (v2.5.53+) Levels of which you would like to sync folders display names between source and grouper. It means you would specify an integer value that represents the level starting from 1 for the group extension. The higher you go up, the more nodes will sync. For example, if a group's path is a:b:c:d:group1 and levels value is 3, display names will sync starting from folder c. If you want to sync everything for this example, choose 5 or higher (or set 'root' in grouperLoaderDisplayNameSyncBaseFolderName).
Configure a loadable group (obviously any number of dynamic loadable groups can exist at once)
With GSH, it would look like this:
You can also use the UI, here are screenshots (obviously these need some work).
Run grouper daemon
The first time you run, it will probably fail, and give you DDL in the logs to run in your database (to add a couple of tables). Run the scripts and you should be all set.
This will kick off as a command line program that you will want to run as a service. This process will be always running, and the scheduler will schedule the jobs. You should monitor the process with a monitoring tool like nagios or whatever you use at your institution so that you know when it is not up.
You can also run a one-timer via gsh. This is useful to run once at the beginning, and not have to wait for the schedule. Or to troubleshoot e.g.
Note that as of Grouper 2.3, if you create a new loader job, you can have it scheduled without restarting the daemon by using an option in the New UI. If you're an admin of the group, under "More actions", there's an option named "Schedule loader process". This option can also be used if you change the loader's schedule.
Groups as members
If you are using the loader to load hierarchies where groups are members, then you need to have a query which has the column SUBJECT_IDENTIFIER with the group system name, or SUBJECT_ID with the group uuid. Useful for org charts. See this example
Logging of jobs in DB
Each job (and subjob if the job manages multiple things) will have an entry in the grouper_loader_log table. This will show the following information. This can be used to tune performance problems, see which jobs have unresolvable subjects, verify that jobs are running, etc.
You can also look at log4j debug log messages, and info log messages (less frequent). to see these, set log level in log4j.properties
## Log debug info on loader to see progress etc
log4j.logger.edu.internet2.middleware.grouper.app.loader = INFO
log4j.logger.edu.internet2.middleware.grouper.app.loader = DEBUG
- You can set transaction level in the grouper-loader.properties. It defaults to not use transaction since for huge groups, you might have memory or db problems
- By default only wheel group members can edit the grouperLoader type or attributes. You can edit these settings in the grouper.properties in the type security part.
Example test proving that only certain members can edit loader attributes
create the security group in gsh
make sure subject not wheel
add a group for that user to be an admin of (GSH)
auto add attributes in grouper-loader.properties
start the loader to init the attributes (command line)
login to UI as test.subject.0, Try to add grouperLoader type to group, and get this error:
In the UI see that the type is not assigned. Check the DB if you don't believe:
Verify the SQL, when I look in grouperSql.log, I see these SQLs:
The next commit or rollback in the file is a rollback (after it checks to see if the user is the member of the allowed group or wheel or GrouperSysadmin)
Unschedule a database quartz job
Note, you can look in the table grouper_qz_triggers and grouper_qz_job_details for more info
If you want to unschedule BLOCKED jobs (the code part is one line of GSH)
If you get errors about a database quartz job, you can unschedule with (or whatever the trigger name is):
These are the default settings:
So for a simple job (SQL_SIMPLE, LDAP_SIMPLE), you can have up to 10 threads. However for a single groupList job (SQL_GROUP_LIST, LDAP_GROUP_LIST, LDAP_GROUPS_FROM_ATTRIBUTES), you can have up to 200 threads since each of the 20 group threads can have 10 membership threads.
This table reflects loader permissions in Grouper v2.5.53+
|Resource||Misc Loader screen||Readonly Loader functions||Read/write Loader function|
|Description||Screen with all loader jobs listed with config and status||Some functions on each loader job||See and edit the loader configuration for a loader job|
* if has VIEW on group
* if has VIEW on group
* if has VIEW on group
Some other settings
|uiV2.group.allowGroupAdminsToRefreshLoaderJobs||If someone is in the configured group, and they are admin on a group, then they can run that loader job|
Possible to do's
- add subject source to group attribute (or default at least) so it doesn't have to be a sql column if all are the same
- make specific blackout times (runtime via config file?)
- make jobs based on person trigger. If there is a query that says when people change, then update that person's memberships in the groups that are dynamic based on that person-change-query
- make full refresh jobs for the incremental jobs (e.g. weekly)
- make an RMI server so that interactions can happen at runtime (to see status, stop/start jobs, etc). Maybe this would happen from gsh
- try the name pattern after loader is done, and if the number of groups is less than the number of groups in this round of loader, set the job status to WARNING and add a descriptive message