mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
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:
parent
cc19b5a76a
commit
f24f614396
@ -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.
|
||||
|
10
install/updates/05-pre_upgrade_plugins.update
Normal file
10
install/updates/05-pre_upgrade_plugins.update
Normal 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
|
20
install/updates/90-post_upgrade_plugins.update
Normal file
20
install/updates/90-post_upgrade_plugins.update
Normal 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
|
@ -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 = \
|
||||
|
@ -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(
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -20,9 +20,3 @@
|
||||
"""
|
||||
Provide a separate api for updates.
|
||||
"""
|
||||
PRE_UPDATE = 1
|
||||
POST_UPDATE = 2
|
||||
|
||||
FIRST = 1
|
||||
MIDDLE = 2
|
||||
LAST = 4
|
||||
|
@ -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,
|
||||
|
@ -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__()
|
||||
|
@ -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))'
|
||||
|
@ -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:
|
||||
|
@ -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")
|
||||
|
@ -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)
|
||||
|
@ -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)"
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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'),
|
||||
|
@ -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:
|
||||
|
@ -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)"
|
||||
|
@ -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 = (
|
||||
"(&"
|
||||
|
@ -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)
|
@ -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:
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user