Server Upgrade: specify order of plugins in update files

* add 'plugin' directive
* specify plugins order in update files
* remove 'run plugins' options
* use ldapupdater API instance in plugins
* add update files representing former PreUpdate and PostUpdate order of plugins

https://fedorahosted.org/freeipa/ticket/4904

Reviewed-By: David Kupka <dkupka@redhat.com>
This commit is contained in:
Martin Basti 2015-03-18 15:46:00 +01:00 committed by Petr Vobornik
parent cc19b5a76a
commit f24f614396
25 changed files with 236 additions and 335 deletions

View File

@ -69,7 +69,11 @@ A few rules:
6. If a DN does exist the default values are skipped
7. Only the first rule on a line is respected
Adds and updates are applied from shortest to longest length of DN. Deletes are done from longest to shortest.
ipa-ldap-updater allows to execute update plugins.
Plugins to be executed are specified with following keyword, in update files:
* plugin: name of plugin
This keyword is not bounded to DN, and plugin names have to be registered in API.
Additionally, ipa-ldap-updater can update the schema based on LDIF files.
Any missing object classes and attribute types are added, and differing ones are updated to match the LDIF file.
@ -81,11 +85,8 @@ Schema files should be in LDIF format, and may only specify attributeTypes and o
\fB\-d\fR, \fB\-\-debug\fR
Enable debug logging when more verbose output is needed
.TP
\fB\-p\fR, \fB\-\-plugins\fR
Execute update plugins as well as any update files. There is no way to execute only the plugins.
.TP
\fB\-u\fR, \fB\-\-upgrade\fR
Upgrade an installed server in offline mode (implies \-\-ldapi, \-\-plugins, and \-\-schema)
Upgrade an installed server in offline mode (implies \-\-schema)
.TP
\fB\-s\fR, \fB\-\-schema\fR
Also update the LDAP schema. If no \-\-schema-file is specified, update to the built-in IPA schema.

View File

@ -0,0 +1,10 @@
# first
plugin: update_managed_post_first
# middle
plugin: update_replica_attribute_lists
plugin: update_passync_privilege_check
plugin: update_referint
plugin: update_uniqueness_plugins_to_new_syntax
# last

View File

@ -0,0 +1,20 @@
# first
# middle
plugin: update_dnszones
plugin: update_dns_limits
plugin: update_default_range
plugin: update_default_trust_view
plugin: update_ca_renewal_master
plugin: update_idrange_type
plugin: update_pacs
plugin: update_service_principalalias
plugin: update_upload_cacrt
# last
plugin: update_master_to_dnsforwardzones
plugin: update_managed_post
plugin: update_managed_permissions
plugin: update_idrange_baserid
plugin: update_passync_privilege_update

View File

@ -2,6 +2,7 @@ NULL =
appdir = $(IPA_DATA_DIR)/updates
app_DATA = \
05-pre_upgrade_plugins.update \
10-config.update \
10-enable-betxn.update \
10-selinuxusermap.update \
@ -47,6 +48,7 @@ app_DATA = \
61-trusts-s4u2proxy.update \
62-ranges.update \
71-idviews.update \
90-post_upgrade_plugins.update \
$(NULL)
EXTRA_DIST = \

View File

@ -1371,7 +1371,7 @@ class Method(Attribute, Command):
@register.base()
class Updater(Method):
class Updater(Plugin):
"""
An LDAP update with an associated object (always update).
@ -1397,8 +1397,8 @@ class Updater(Method):
>>> api.Updater.my_update # doctest:+ELLIPSIS
ipalib.frontend.my_update()
"""
def __init__(self):
super(Updater, self).__init__()
def execute(self, **options):
raise NotImplementedError('%s.execute()' % self.name)
def __call__(self, **options):
self.debug(

View File

@ -507,7 +507,8 @@ class DsInstance(service.Service):
conn.unbind()
def apply_updates(self):
ld = ldapupdate.LDAPUpdate(dm_password=self.dm_password, sub_dict=self.sub_dict, plugins=True)
ld = ldapupdate.LDAPUpdate(dm_password=self.dm_password,
sub_dict=self.sub_dict)
files = ld.get_all_files(ldapupdate.UPDATES_DIR)
ld.update(files)

View File

@ -48,10 +48,6 @@ class LDAPUpdater(admintool.AdminTool):
parser.add_option("-u", '--upgrade', action="store_true",
dest="upgrade", default=False,
help="upgrade an installed server in offline mode")
parser.add_option("-p", '--plugins', action="store_true",
dest="plugins", default=False,
help="execute update plugins " +
"(implied when no input files are given)")
parser.add_option("-s", '--schema', action="store_true",
dest="update_schema", default=False,
help="update the schema "
@ -140,10 +136,6 @@ class LDAPUpdater_NonUpgrade(LDAPUpdater):
def validate_options(self):
super(LDAPUpdater_NonUpgrade, self).validate_options()
options = self.options
# Only run plugins if no files are given
self.run_plugins = not self.files or options.plugins
# Need root for running plugins
if os.getegid() != 0:
@ -167,8 +159,7 @@ class LDAPUpdater_NonUpgrade(LDAPUpdater):
ld = LDAPUpdate(
sub_dict={},
ldapi=True,
plugins=options.plugins or self.run_plugins)
ldapi=True)
if not self.files:
self.files = ld.get_all_files(UPDATES_DIR)

View File

@ -36,13 +36,14 @@ import krbV
import ldap
from ipaserver.install import installutils
from ipaserver.install.plugins.baseupdate import DSRestart
from ipapython import ipautil, ipaldap
from ipalib import errors
from ipalib import api
from ipalib import api, create_api
from ipaplatform.paths import paths
from ipapython.dn import DN
from ipapython.ipa_log_manager import *
from ipaserver.install.plugins import (PRE_UPDATE, POST_UPDATE)
from ipapython.ipautil import wait_for_open_socket
from ipaserver.plugins import ldap2
UPDATES_DIR=paths.UPDATES_DIR
@ -113,7 +114,7 @@ class LDAPUpdate:
action_keywords = ["default", "add", "remove", "only", "onlyifexist", "deleteentry", "replace", "addifnew", "addifexist"]
def __init__(self, dm_password=None, sub_dict={},
online=True, ldapi=False, plugins=False):
online=True, ldapi=False):
'''
:parameters:
dm_password
@ -124,8 +125,6 @@ class LDAPUpdate:
Do an online LDAP update or use an experimental LDIF updater
ldapi
Bind using ldapi. This assumes autobind is enabled.
plugins
execute the pre/post update plugins
Data Structure Example:
-----------------------
@ -152,6 +151,67 @@ class LDAPUpdate:
The default and update lists are "dispositions"
Plugins:
Plugins has to be specified in update file to be executed, using
'plugin' directive
Example:
plugin: update_uniqueness_plugins_to_new_syntax
Each plugin returns two values:
1. restart: dirsrv will be restarted AFTER this update is
applied.
2. updates: A list of updates to be applied.
The value of an update is a dictionary with the following possible
values:
- dn: DN, equal to the dn attribute
- updates: list of updates against the dn
- default: list of the default entry to be added if it doesn't
exist
- deleteentry: list of dn's to be deleted (typically single dn)
For example, this update file:
dn: cn=global_policy,cn=$REALM,cn=kerberos,$SUFFIX
replace:krbPwdLockoutDuration:10::600
replace: krbPwdMaxFailure:3::6
Generates this list which contain the update dictionary:
[
dict(
'dn': 'cn=global_policy,cn=EXAMPLE.COM,cn=kerberos,dc=example,dc=com',
'updates': ['replace:krbPwdLockoutDuration:10::600',
'replace:krbPwdMaxFailure:3::6']
)
]
Here is another example showing how a default entry is configured:
dn: cn=Managed Entries,cn=etc,$SUFFIX
default: objectClass: nsContainer
default: objectClass: top
default: cn: Managed Entries
This generates:
[
dict(
'dn': 'cn=Managed Entries,cn=etc,dc=example,dc=com',
'default': ['objectClass:nsContainer',
'objectClass:top',
'cn:Managed Entries'
]
)
]
Note that the variable substitution in both examples has been completed.
Either may make changes directly in LDAP or can return updates in
update format.
'''
log_mgr.get_logger(self, True)
@ -161,9 +221,15 @@ class LDAPUpdate:
self.modified = False
self.online = online
self.ldapi = ldapi
self.plugins = plugins
self.pw_name = pwd.getpwuid(os.geteuid()).pw_name
self.realm = None
self.socket_name = (
paths.SLAPD_INSTANCE_SOCKET_TEMPLATE %
api.env.realm.replace('.', '-')
)
self.ldapuri = 'ldapi://%s' % ipautil.format_netloc(
self.socket_name
)
suffix = None
if sub_dict.get("REALM"):
@ -202,13 +268,14 @@ class LDAPUpdate:
self.sub_dict["TIME"] = int(time.time())
if not self.sub_dict.get("DOMAIN") and domain is not None:
self.sub_dict["DOMAIN"] = domain
self.api = create_api(mode=None)
self.api.bootstrap(in_server=True, context='updates')
self.api.finalize()
if online:
# Try out the connection/password
# (This will raise if the server is not available)
self.create_connection()
self.conn.unbind()
self.conn = None
self.close_connection()
else:
raise RuntimeError("Offline updates are not supported.")
@ -333,6 +400,13 @@ class LDAPUpdate:
assert isinstance(dn, DN)
all_updates.append(update)
def emit_plugin_update(update):
'''
When processing a plugin is complete emit the plugin update by
appending it into list of all updates
'''
all_updates.append(update)
# Iterate over source input lines
for source_line in source_data:
lcount += 1
@ -344,18 +418,38 @@ class LDAPUpdate:
if source_line.startswith('#') or source_line == '':
continue
if source_line.lower().startswith('dn:'):
# Starting new dn
if dn is not None:
# Emit previous dn
emit_item(logical_line)
logical_line = ''
emit_update(update)
update = {}
state = None
emit_previous_dn = False
# parse special keywords
if source_line.lower().startswith('dn:'):
state = 'dn'
emit_previous_dn = True
elif source_line.lower().startswith('plugin:'):
state = 'plugin'
emit_previous_dn = True
if emit_previous_dn and dn is not None:
# Emit previous dn
emit_item(logical_line)
logical_line = ''
emit_update(update)
update = {}
dn = None
if state == 'dn':
# Starting new dn
dn = source_line[3:].strip()
dn = DN(self._template_str(dn))
update['dn'] = dn
elif state == 'plugin':
# plugin specification is online only
plugin_name = source_line[7:].strip()
if not plugin_name:
raise BadSyntax("plugin name is not defined")
update['plugin'] = plugin_name
emit_plugin_update(update)
update = {}
else:
# Process items belonging to dn
if dn is None:
@ -589,10 +683,6 @@ class LDAPUpdate:
def _update_record(self, update):
found = False
# If the entry is going to be deleted no point in processing it.
if update.has_key('deleteentry'):
return
new_entry = self._create_default_entry(update.get('dn'),
update.get('default'))
@ -687,9 +777,6 @@ class LDAPUpdate:
and child in the wrong order.
"""
if not updates.has_key('deleteentry'):
return
dn = updates['dn']
try:
self.info("Deleting entry %s", dn)
@ -713,20 +800,36 @@ class LDAPUpdate:
f.sort()
return f
def _run_update_plugin(self, plugin_name):
self.log.info("Executing upgrade plugin: %s", plugin_name)
restart_ds, updates = self.api.Updater[plugin_name]()
if updates:
self._run_updates(updates)
# restart may be required even if no updates were returned
# from plugin, plugin may change LDAP data directly
if restart_ds:
self.close_connection()
self.restart_ds()
self.create_connection()
def create_connection(self):
if self.online:
self.conn = connect(
ldapi=self.ldapi, realm=self.realm, fqdn=self.sub_dict['FQDN'],
dm_password=self.dm_password, pw_name=self.pw_name)
self.api.Backend.ldap2.connect(
bind_dn=DN(('cn', 'Directory Manager')),
bind_pw=self.dm_password,
autobind=self.ldapi)
self.conn = self.api.Backend.ldap2
else:
raise RuntimeError("Offline updates are not supported.")
def _run_updates(self, all_updates):
for update in all_updates:
self._update_record(update)
for update in all_updates:
self._delete_record(update)
if 'deleteentry' in update:
self._delete_record(update)
elif 'plugin' in update:
self._run_update_plugin(update['plugin'])
else:
self._update_record(update)
def update(self, files, ordered=True):
"""Execute the update. files is a list of the update files to use.
@ -738,12 +841,6 @@ class LDAPUpdate:
all_updates = []
try:
self.create_connection()
if self.plugins:
self.info('PRE_UPDATE')
updates = api.Backend.updateclient.update(
PRE_UPDATE, self.dm_password, self.ldapi)
# flush out PRE_UPDATE plugin updates before we begin
self._run_updates(updates)
upgrade_files = files
if ordered:
@ -760,18 +857,11 @@ class LDAPUpdate:
self.parse_update_file(f, data, all_updates)
self._run_updates(all_updates)
all_updates = []
if self.plugins:
self.info('POST_UPDATE')
updates = api.Backend.updateclient.update(
POST_UPDATE, self.dm_password, self.ldapi)
self._run_updates(updates)
finally:
self.close_connection()
return self.modified
def update_from_dict(self, updates):
"""
Apply updates internally as opposed to from a file.
@ -788,5 +878,11 @@ class LDAPUpdate:
def close_connection(self):
"""Close ldap connection"""
if self.conn:
self.conn.unbind()
self.api.Backend.ldap2.disconnect()
self.conn = None
def restart_ds(self):
dsrestart = DSRestart()
dsrestart.create_instance()
wait_for_open_socket(self.socket_name)

View File

@ -20,9 +20,3 @@
"""
Provide a separate api for updates.
"""
PRE_UPDATE = 1
POST_UPDATE = 2
FIRST = 1
MIDDLE = 2
LAST = 4

View File

@ -17,22 +17,20 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from ipaserver.install.plugins import MIDDLE
from ipaserver.install.plugins.baseupdate import PostUpdate
from ipalib import api, errors
from ipalib import Updater
from ipapython.dn import DN
from ipapython.ipa_log_manager import *
DEFAULT_ID_RANGE_SIZE = 200000
class update_default_range(PostUpdate):
class update_default_range(Updater):
"""
Create default ID range for upgraded servers.
"""
order=MIDDLE
def execute(self, **options):
ldap = self.obj.backend
ldap = self.api.Backend.ldap2
dn = DN(api.env.container_ranges, api.env.basedn)
search_filter = "objectclass=ipaDomainIDRange"
@ -117,14 +115,13 @@ class update_default_range(PostUpdate):
return False, [update]
class update_default_trust_view(PostUpdate):
class update_default_trust_view(Updater):
"""
Create Default Trust View for upgraded servers.
"""
order = MIDDLE
def execute(self, **options):
ldap = self.obj.backend
ldap = self.api.Backend.ldap2
default_trust_view_dn = DN(('cn', 'Default Trust View'),
api.env.container_views,

View File

@ -18,9 +18,8 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from ipalib import api
from ipalib import Updater, Object
from ipalib import Object
from ipaserver.install import service
from ipaserver.install.plugins import (PRE_UPDATE, POST_UPDATE, MIDDLE)
class DSRestart(service.Service):
"""
@ -46,32 +45,3 @@ class DSRestart(service.Service):
self.step("starting directory server", self.start)
self.start_creation(start_message="Restarting Directory server "
"to apply updates", show_service_name=False)
class update(Object):
"""
Generic object used to register all updates into a single namespace.
"""
backend_name = 'ldap2'
api.register(update)
class PreUpdate(Updater):
"""
Base class for updates that run prior to file processing.
"""
updatetype = PRE_UPDATE
order = MIDDLE
def __init__(self):
super(PreUpdate, self).__init__()
class PostUpdate(Updater):
"""
Base class for updates that run after file processing.
"""
updatetype = POST_UPDATE
order = MIDDLE
def __init__(self):
super(PostUpdate, self).__init__()

View File

@ -17,9 +17,9 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from ipaserver.install.plugins.baseupdate import PostUpdate
from ipaserver.install import installutils, certs, cainstance
from ipalib import errors
from ipalib import Updater
from ipalib.plugable import Registry
from ipapython import certmonger, dogtag
from ipaplatform.paths import paths
@ -28,7 +28,7 @@ from ipapython.dn import DN
register = Registry()
@register()
class update_ca_renewal_master(PostUpdate):
class update_ca_renewal_master(Updater):
"""
Set CA renewal master in LDAP.
"""
@ -39,7 +39,7 @@ class update_ca_renewal_master(PostUpdate):
self.debug("CA is not configured on this host")
return False, []
ldap = self.obj.backend
ldap = self.api.Backend.ldap2
base_dn = DN(('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'),
self.api.env.basedn)
filter = '(&(cn=CA)(ipaConfigString=caRenewalMaster))'

View File

@ -24,16 +24,14 @@ import time
from ldif import LDIFWriter
from ipaserver.install.plugins import MIDDLE, LAST
from ipaserver.install.plugins.baseupdate import (PostUpdate, PreUpdate)
from ipaserver.install import sysupgrade
from ipalib import api, errors, util
from ipalib import Updater
from ipapython.dn import DN
from ipalib.plugins.dns import dns_container_exists
from ipapython.ipa_log_manager import *
class update_dnszones(PostUpdate):
class update_dnszones(Updater):
"""
Update all zones to meet requirements in the new FreeIPA versions
@ -57,10 +55,9 @@ class update_dnszones(PostUpdate):
This module extends the original policy to allow the SSHFP updates.
"""
order=MIDDLE
def execute(self, **options):
ldap = self.obj.backend
ldap = self.api.Backend.ldap2
if not dns_container_exists(ldap):
return False, []
@ -95,7 +92,7 @@ class update_dnszones(PostUpdate):
api.register(update_dnszones)
class update_dns_limits(PostUpdate):
class update_dns_limits(Updater):
"""
bind-dyndb-ldap persistent search queries LDAP for all DNS records.
The LDAP connection must have no size or time limits to work
@ -106,7 +103,7 @@ class update_dns_limits(PostUpdate):
limit_value = '-1'
def execute(self, **options):
ldap = self.obj.backend
ldap = self.api.Backend.ldap2
if not dns_container_exists(ldap):
return False, []
@ -142,7 +139,7 @@ class update_dns_limits(PostUpdate):
api.register(update_dns_limits)
class update_master_to_dnsforwardzones(PostUpdate):
class update_master_to_dnsforwardzones(Updater):
"""
Update all zones to meet requirements in the new FreeIPA versions
@ -152,14 +149,12 @@ class update_master_to_dnsforwardzones(PostUpdate):
This should be applied only once, and only if original version was lower than 4.0
"""
order = LAST
backup_dir = u'/var/lib/ipa/backup/'
backup_filename = u'dns-forward-zones-backup-%Y-%m-%d-%H-%M-%S.ldif'
backup_path = u'%s%s' % (backup_dir, backup_filename)
def execute(self, **options):
ldap = self.obj.backend
ldap = self.api.Backend.ldap2
# check LDAP if forwardzones already uses new semantics
dns_container_dn = DN(api.env.container_dns, api.env.basedn)
try:

View File

@ -20,23 +20,20 @@
import os
import pwd
from ipapython import ipaldap
from ipaserver.install.plugins import MIDDLE
from ipaserver.install.plugins.baseupdate import PreUpdate
from ipaserver.install import replication
from ipalib import api
from ipalib import Updater
EXCLUDE_TEMPLATE = '(objectclass=*) $ EXCLUDE %s'
class update_replica_attribute_lists(PreUpdate):
class update_replica_attribute_lists(Updater):
"""
Run through all replication agreements and ensure that EXCLUDE list
has all the required attributes so that we don't cause replication
storms.
"""
order = MIDDLE
def execute(self, **options):
# We need an IPAdmin connection to the backend
self.log.debug("Start replication agreement exclude list update task")

View File

@ -17,9 +17,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from ipaserver.install.plugins import FIRST, LAST
from ipaserver.install.plugins.baseupdate import PreUpdate, PostUpdate
from ipalib import api, errors
from ipalib import Updater
from ipapython import ipautil
from ipapython.dn import DN, EditableDN
@ -47,7 +46,7 @@ class GenerateUpdateMixin(object):
We need to separate the deletes that need to happen from the
new entries that need to be added.
"""
ldap = self.obj.backend
ldap = self.api.Backend.ldap2
suffix = ipautil.realm_to_suffix(api.env.realm)
searchfilter = '(objectclass=*)'
@ -134,11 +133,10 @@ class GenerateUpdateMixin(object):
return (restart, update_list)
class update_managed_post_first(PreUpdate, GenerateUpdateMixin):
class update_managed_post_first(Updater, GenerateUpdateMixin):
"""
Update managed entries
"""
order=FIRST
def execute(self, **options):
# Never need to restart with the pre-update changes
@ -148,11 +146,10 @@ class update_managed_post_first(PreUpdate, GenerateUpdateMixin):
api.register(update_managed_post_first)
class update_managed_post(PostUpdate, GenerateUpdateMixin):
class update_managed_post(Updater, GenerateUpdateMixin):
"""
Update managed entries
"""
order=LAST
def execute(self, **options):
(restart, update_list) = self.generate_update(True)

View File

@ -17,23 +17,20 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from ipaserver.install.plugins import MIDDLE, LAST
from ipaserver.install.plugins.baseupdate import PostUpdate
from ipalib import api, errors
from ipalib import Updater
from ipapython.dn import DN
from ipapython.ipa_log_manager import *
class update_idrange_type(PostUpdate):
class update_idrange_type(Updater):
"""
Update all ID ranges that do not have ipaRangeType attribute filled.
This applies to all ID ranges prior to IPA 3.3.
"""
order = MIDDLE
def execute(self, **options):
ldap = self.obj.backend
ldap = self.api.Backend.ldap2
base_dn = DN(api.env.container_ranges, api.env.basedn)
search_filter = ("(&(objectClass=ipaIDrange)(!(ipaRangeType=*)))")
@ -112,16 +109,14 @@ class update_idrange_type(PostUpdate):
return False, []
class update_idrange_baserid(PostUpdate):
class update_idrange_baserid(Updater):
"""
Update ipa-ad-trust-posix ranges' base RID to 0. This applies to AD trust
posix ranges prior to IPA 4.1.
"""
order = LAST
def execute(self, **options):
ldap = self.obj.backend
ldap = self.api.Backend.ldap2
base_dn = DN(api.env.container_ranges, api.env.basedn)
search_filter = ("(&(objectClass=ipaTrustedADDomainRange)"

View File

@ -89,11 +89,9 @@ from ipalib.plugable import Registry
from ipalib.plugins import aci
from ipalib.plugins.permission import permission, permission_del
from ipalib.aci import ACI
from ipalib import Updater
from ipapython import ipautil
from ipaserver.plugins.ldap2 import ldap2
from ipaserver.install.plugins import LAST
from ipaserver.install.plugins.baseupdate import PostUpdate
register = Registry()
@ -349,14 +347,13 @@ class IncompatibleACIModification(Exception):
@register()
class update_managed_permissions(PostUpdate):
class update_managed_permissions(Updater):
"""Update managed permissions after an update.
Update managed permissions according to templates specified in plugins.
For read permissions, puts any attributes specified in the legacy
Anonymous access ACI in the exclude list when creating the permission.
"""
order = LAST
def get_anonymous_read_aci(self, ldap):
aciname = u'Enable Anonymous access'
@ -402,7 +399,7 @@ class update_managed_permissions(PostUpdate):
def execute(self, **options):
ldap = self.api.Backend[ldap2]
ldap = self.api.Backend.ldap2
anonymous_read_aci = self.get_anonymous_read_aci(ldap)

View File

@ -17,21 +17,18 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from ipaserver.install.plugins import MIDDLE
from ipaserver.install.plugins.baseupdate import PostUpdate
from ipalib import api, errors
from ipalib import Updater
from ipapython.dn import DN
class update_pacs(PostUpdate):
class update_pacs(Updater):
"""
Includes default nfs:None only if no nfs: PAC present in ipakrbauthzdata.
"""
order = MIDDLE
def execute(self, **options):
ldap = self.obj.backend
ldap = self.api.Backend.ldap2
try:
dn = DN('cn=ipaConfig', 'cn=etc', api.env.basedn)

View File

@ -2,15 +2,13 @@
# Copyright (C) 2014 FreeIPA Contributors see COPYING for license
#
from ipaserver.install.plugins import MIDDLE, LAST
from ipaserver.install.plugins.baseupdate import PreUpdate, PostUpdate
from ipalib import api, errors
from ipalib import Updater
from ipapython.dn import DN
from ipapython.ipa_log_manager import root_logger
from ipaserver.install import sysupgrade
class update_passync_privilege_check(PreUpdate):
order = MIDDLE
class update_passync_privilege_check(Updater):
def execute(self, **options):
update_done = sysupgrade.get_upgrade_state('winsync', 'passsync_privilege_updated')
@ -24,7 +22,7 @@ class update_passync_privilege_check(PreUpdate):
self.api.env.container_privilege,
self.api.env.basedn)
ldap = self.obj.backend
ldap = self.api.Backend.ldap2
try:
ldap.get_entry(passsync_privilege_dn, [''])
except errors.NotFound:
@ -38,13 +36,11 @@ class update_passync_privilege_check(PreUpdate):
api.register(update_passync_privilege_check)
class update_passync_privilege_update(PostUpdate):
class update_passync_privilege_update(Updater):
"""
Add PassSync user as a member of PassSync privilege, if it exists
"""
order = LAST
def execute(self, **options):
update_done = sysupgrade.get_upgrade_state('winsync', 'passsync_privilege_updated')
if update_done:
@ -52,7 +48,7 @@ class update_passync_privilege_update(PostUpdate):
return False, []
root_logger.debug("Add PassSync user as a member of PassSync privilege")
ldap = self.obj.backend
ldap = self.api.Backend.ldap2
passsync_dn = DN(('uid','passsync'), ('cn', 'sysaccounts'), ('cn', 'etc'),
api.env.basedn)
passsync_privilege_dn = DN(('cn','PassSync Service'),

View File

@ -2,13 +2,12 @@
# Copyright (C) 2014 FreeIPA Contributors see COPYING for license
#
from ipaserver.install.plugins import MIDDLE
from ipaserver.install.plugins.baseupdate import PreUpdate
from ipalib import api, errors
from ipalib import Updater
from ipapython.dn import DN
from ipapython.ipa_log_manager import root_logger
class update_referint(PreUpdate):
class update_referint(Updater):
"""
Update referential integrity configuration to new style
http://directory.fedoraproject.org/docs/389ds/design/ri-plugin-configuration.html
@ -22,15 +21,13 @@ class update_referint(PreUpdate):
Old and new style cannot be mixed, all nslapd-pluginArg* attrs have to be removed
"""
order = MIDDLE
referint_dn = DN(('cn', 'referential integrity postoperation'),
('cn', 'plugins'), ('cn', 'config'))
def execute(self, **options):
root_logger.debug("Upgrading referential integrity plugin configuration")
ldap = self.obj.backend
ldap = self.api.Backend.ldap2
try:
entry = ldap.get_entry(self.referint_dn)
except errors.NotFound:

View File

@ -17,23 +17,21 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from ipaserver.install.plugins import MIDDLE
from ipaserver.install.plugins.baseupdate import PostUpdate
from ipalib import api, errors
from ipalib import Updater
from ipapython.dn import DN
from ipapython.ipa_log_manager import *
class update_service_principalalias(PostUpdate):
class update_service_principalalias(Updater):
"""
Update all services which do not have ipakrbprincipalalias attribute
used for case-insensitive principal searches filled. This applies for
all services created prior IPA 3.0.
"""
order = MIDDLE
def execute(self, **options):
ldap = self.obj.backend
ldap = self.api.Backend.ldap2
base_dn = DN(api.env.container_service, api.env.basedn)
search_filter = ("(&(objectclass=krbprincipal)(objectclass=ipaservice)"

View File

@ -17,14 +17,13 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from ipaserver.install.plugins import MIDDLE
from ipaserver.install.plugins.baseupdate import PostUpdate, PreUpdate
from ipalib import api, errors
from ipalib import Updater
from ipapython.dn import DN
from ipapython.ipa_log_manager import *
class update_uniqueness_plugins_to_new_syntax(PreUpdate):
class update_uniqueness_plugins_to_new_syntax(Updater):
"""
Migrate uniqueness plugins to new style syntax
@ -165,7 +164,7 @@ class update_uniqueness_plugins_to_new_syntax(PreUpdate):
return update
def execute(self, **options):
ldap = self.obj.backend
ldap = self.api.Backend.ldap2
old_style_plugin_search_filter = (
"(&"

View File

@ -1,147 +0,0 @@
# Authors: Rob Crittenden <rcritten@redhat.com>
#
# Copyright (C) 2011 Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from ipaserver.install.plugins.baseupdate import DSRestart
from ipaserver.install.ldapupdate import LDAPUpdate
from ipapython.ipautil import wait_for_open_socket
from ipalib import api
from ipalib import backend
from ipaplatform.paths import paths
from ipapython.dn import DN
class updateclient(backend.Executioner):
"""
Backend used for applying LDAP updates via plugins
An update plugin can be executed before the file-based plugins or
afterward. Each plugin returns three values:
1. restart: dirsrv will be restarted AFTER this update is
applied.
2. updates: A list of updates to be applied.
The value of an update is a dictionary with the following possible
values:
- dn: DN, equal to the dn attribute
- updates: list of updates against the dn
- default: list of the default entry to be added if it doesn't
exist
- deleteentry: list of dn's to be deleted (typically single dn)
For example, this update file:
dn: cn=global_policy,cn=$REALM,cn=kerberos,$SUFFIX
replace:krbPwdLockoutDuration:10::600
replace: krbPwdMaxFailure:3::6
Generates this list which contain the update dictionary:
[
dict(
'dn': 'cn=global_policy,cn=EXAMPLE.COM,cn=kerberos,dc=example,dc=com',
'updates': ['replace:krbPwdLockoutDuration:10::600',
'replace:krbPwdMaxFailure:3::6']
)
]
Here is another example showing how a default entry is configured:
dn: cn=Managed Entries,cn=etc,$SUFFIX
default: objectClass: nsContainer
default: objectClass: top
default: cn: Managed Entries
This generates:
[
dict(
'dn': 'cn=Managed Entries,cn=etc,dc=example,dc=com',
'default': ['objectClass:nsContainer',
'objectClass:top',
'cn:Managed Entries'
]
)
]
Note that the variable substitution in both examples has been completed.
A PRE_UPDATE plugin is executed before file-based updates.
A POST_UPDATE plugin is executed after file-based updates.
Plugins are executed automatically when ipa-ldap-updater is run
in upgrade mode (--upgrade). They are not executed normally otherwise.
To execute plugins as well use the --plugins flag.
Either may make changes directly in LDAP or can return updates in
update format.
"""
def create_context(self, dm_password):
if dm_password:
autobind = False
else:
autobind = True
self.Backend.ldap2.connect(bind_dn=DN(('cn', 'Directory Manager')), bind_pw=dm_password, autobind=autobind)
def order(self, updatetype):
"""Return plugins of the given updatetype in sorted order.
"""
ordered = [plugin for plugin in api.Updater()
if plugin.updatetype == updatetype]
ordered.sort(key=lambda p: p.order)
return ordered
def update(self, updatetype, dm_password, ldapi):
"""
Execute all update plugins of type updatetype.
"""
self.create_context(dm_password)
kw = dict()
result = []
ld = LDAPUpdate(dm_password=dm_password, sub_dict={}, ldapi=ldapi)
for update in self.order(updatetype):
restart, res = self.run(update.name, **kw)
ld.update_from_dict(res)
if restart:
# connection has to be closed before restart, otherwise
# ld instance will try to reuse old non-valid connection
ld.close_connection()
self.restart(dm_password)
self.destroy_context()
return result
def run(self, method, **kw):
"""
Execute the update plugin.
"""
return self.Updater[method](**kw)
def restart(self, dm_password):
dsrestart = DSRestart()
socket_name = paths.SLAPD_INSTANCE_SOCKET_TEMPLATE % \
api.env.realm.replace('.','-')
self.destroy_context()
dsrestart.create_instance()
wait_for_open_socket(socket_name)
self.create_context(dm_password)
api.register(updateclient)

View File

@ -17,18 +17,16 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from ipaserver.install.plugins import MIDDLE
from ipaserver.install.plugins.baseupdate import PostUpdate
from ipaserver.install import certs
from ipalib import api, errors, certstore
from ipalib import Updater
from ipapython import certdb
from ipapython.dn import DN
class update_upload_cacrt(PostUpdate):
class update_upload_cacrt(Updater):
"""
Upload public CA certificate to LDAP
"""
order=MIDDLE
def execute(self, **options):
db = certs.CertDB(self.api.env.realm)
@ -45,7 +43,7 @@ class update_upload_cacrt(PostUpdate):
if ca_chain:
ca_nickname = ca_chain[-1]
ldap = self.obj.backend
ldap = self.api.Backend.ldap2
for nickname, trust_flags in db.list_certs():
if 'u' in trust_flags:

View File

@ -126,7 +126,7 @@ class IPAUpgrade(service.Service):
def __upgrade(self):
try:
ld = ldapupdate.LDAPUpdate(dm_password='', ldapi=True, plugins=True)
ld = ldapupdate.LDAPUpdate(dm_password='', ldapi=True)
if len(self.files) == 0:
self.files = ld.get_all_files(ldapupdate.UPDATES_DIR)
self.modified = (ld.update(self.files) or self.modified)