Index: umc/setup.xml =================================================================== --- umc/setup.xml (Revision 43637) +++ umc/setup.xml (Arbeitskopie) @@ -20,7 +20,6 @@ - Index: umc/python/setup/__init__.py =================================================================== --- umc/python/setup/__init__.py (Revision 43637) +++ umc/python/setup/__init__.py (Arbeitskopie) @@ -65,7 +65,6 @@ RE_IPV4 = re.compile(r'^interfaces/(([^/_]+)(_[0-9])?)/(address|netmask)$') RE_IPV6_DEFAULT = re.compile(r'^interfaces/([^/]+)/ipv6/default/(prefix|address)$') -RE_SPACE = re.compile(r'\s+') RE_SSL = re.compile(r'^ssl/.*') class Instance(umcm.Base): @@ -79,6 +78,7 @@ os.umask( 0022 ) def init( self ): + locale.setlocale( locale.LC_ALL, str( self.locale ) ) util.installer_i18n.set_language( str( self.locale ) ) os.environ[ 'LC_ALL' ] = str( self.locale ) @@ -344,8 +344,8 @@ _check('server/role', lambda x: not(orgValues.get('joined')) or (orgValues.get('server/role') == values.get('server/role')), _('The system role may not change on a system that has already joined to domain.')) # basis - components = RE_SPACE.split(values.get('components', '')) - packages = set(reduce(lambda x, y: x + y, [ i.split(':') for i in components ])) + components = values.get('components', []) + packages = set(reduce(lambda x, y: x + y, [ comp.get('defaultpackages', []) for comp in components if comp.get('is_installed')])) _check('hostname', util.is_hostname, _('The hostname is not a valid fully qualified domain name in lowercase (e.g. host.example.com).')) _check('hostname', lambda x: len(x) <= 13, _('A valid NetBIOS name can not be longer than 13 characters. If Samba is installed, the hostname should be shortened.'), critical=('univention-samba' in packages or 'univention-samba4' in packages)) @@ -522,10 +522,3 @@ "interface" and optionally the key "timeout" (in seconds).''' return util.dhclient(interface, timeout) - - @simple_response - def software_components(self, role=None): - '''Return a list of all available software packages. Entries have the properties - "id", "label", and "packages" which is an array of the Debian package names.''' - return [ { 'id': i['id'], 'label': i['Name'], 'packages': i['Packages'] } - for i in util.get_components(role=role) ] Index: umc/python/setup/util.py =================================================================== --- umc/python/setup/util.py (Revision 43637) +++ umc/python/setup/util.py (Arbeitskopie) @@ -40,22 +40,15 @@ import univention.config_registry import time import re -import sys -import apt import psutil import csv -import imp import os.path -from univention.lib.i18n import Translation from univention.management.console.log import MODULE -installer_i18n = Translation( 'installer', localedir = '/lib/univention-installer/locale' ) +from univention.management.console.modules.appcenter.app_center import Application +from univention.lib.package_manager import PackageManager -if not '/lib/univention-installer/' in sys.path: - sys.path.append('/lib/univention-installer/') -import package_list - ucr=univention.config_registry.ConfigRegistry() ucr.load() @@ -73,6 +66,13 @@ RE_IPV4_TYPE = re.compile('interfaces/[^/]*/type') RE_LOCALE = re.compile(r'([^.@ ]+).*') +read_only_package_manager = PackageManager( + info_handler=MODULE.process, + step_handler=None, + error_handler=MODULE.warn, + lock=False, +) + # list of all needed UCR variables UCR_VARIABLES = [ # common @@ -123,8 +123,8 @@ else: values['timezone']='' - # get installed components - values['components'] = ' '.join([icomp['id'] for icomp in get_installed_components()]) + # get components + values['components'] = get_components() return values @@ -160,20 +160,18 @@ # add lists with all packages that should be removed/installed on the system if 'components' in newValues: - regSpaces = re.compile(r'\s+') - selectedComponents = set(regSpaces.split(newValues.get('components', ''))) - currentComponents = set([icomp['id'] for icomp in get_installed_components()]) - allComponents = set([ icomp['id'] for icomp in get_components() ]) + components_selected = dict([comp['id'], comp.get('is_installed', False)] for comp in newValues['components']) + all_components = get_components() # get all packages that shall be removed - removeComponents = list(allComponents & (currentComponents - selectedComponents)) - newValues['packages_remove'] = ' '.join([ i.replace(':', ' ') for i in removeComponents ]) + remove_apps = [ app for app in all_components if app['is_installed'] and not components_selected.get(app['id'], False) ] + newValues['apps_remove'] = ' '.join([app['id'] for app in remove_apps]) - allComponents = set([ icomp['id'] for icomp in get_components(role=role) ]) + all_components = get_components(role=role) # get all packages that shall be installed - installComponents = list(allComponents & (selectedComponents - currentComponents)) - newValues['packages_install'] = ' '.join([ i.replace(':', ' ') for i in installComponents ]) + install_apps = [ app for app in all_components if not app['is_installed'] and components_selected.get(app['id'], False) ] + newValues['apps_install'] = ' '.join([app['id'] for app in install_apps]) # if 'locale' in newValues: # # js returns locale as list @@ -235,11 +233,12 @@ # fractions of setup scripts FRACTIONS = { - '10_basis/12domainname' : 5, - '10_basis/14ldap_basis' : 10, - '30_net/10interfaces' : 5, - '30_net/11ipv6interfaces' : 5, - '50_software/10software' : 50, + '10_basis/12domainname' : 5, + '10_basis/14ldap_basis' : 10, + '30_net/10interfaces' : 5, + '30_net/11ipv6interfaces' : 5, + '50_software/10software' : 5, # deprecated, probably not used + '50_software/20apps' : 50, } # current status @@ -552,49 +551,18 @@ return dhcp_dict def get_components(role=None): - '''Returns a list of components that may be installed on the current system.''' + '''Returns a list of all components.''' - # get all package sets that are available for the current system role - if not role: - role = ucr.get('server/role') + components = [] + read_only_package_manager.reopen_cache() + Application._get_category_translations(fake=True) + for app in Application.all(force_reread=True, only_local=True): + if 'UCS components' in app.get('categories'): + if role and app.get('serverrole') and role not in app.get('serverrole'): + continue + components.append(app.to_dict(read_only_package_manager)) + return components - # reload for correct locale - imp.reload(package_list) - pkglist = [ jpackage for icategory in package_list.PackageList - for jpackage in icategory['Packages'] - if 'all' in jpackage['Possible'] or role in jpackage['Possible'] ] - - # filter whitelisted packages - whitelist = ucr.get('system/setup/packages/whitelist') - if whitelist: - whitelist = whitelist.split(' ') - pkglist = [ipkg for ipkg in pkglist if all(jpkg in whitelist for jpkg in ipkg['Packages'])] - - # filter blacklisted packages - blacklist = ucr.get('system/setup/packages/blacklist') - if blacklist: - blacklist = blacklist.split(' ') - pkglist = [ipkg for ipkg in pkglist if not any(jpkg in blacklist for jpkg in ipkg['Packages'])] - - # generate a unique ID for each component - for ipkg in pkglist: - ipkg['id'] = ':'.join(ipkg['Packages']) - ipkg[ 'Description' ] = installer_i18n.translate( ipkg[ 'Description' ] ) - ipkg[ 'Name' ] = installer_i18n.translate( ipkg[ 'Name' ] ) - - return pkglist - -def get_installed_packages(): - '''Returns a list of all installed packages on the system.''' - cache = apt.Cache() - return [ p.name for p in cache if p.is_installed ] - -def get_installed_components(): - '''Returns a list of components that are currently fully installed on the system.''' - allPackages = set(get_installed_packages()) - allComponents = get_components() - return [ icomp for icomp in allComponents if not len(set(icomp['Packages']) - allPackages) ] - # from univention-installer/installer/modules/70_net.py def is_proxy(proxy): if proxy and proxy != 'http://' and proxy != 'https://': Index: umc/js/setup/SoftwarePage.js =================================================================== --- umc/js/setup/SoftwarePage.js (Revision 43637) +++ umc/js/setup/SoftwarePage.js (Arbeitskopie) @@ -33,180 +33,77 @@ "dojo/_base/lang", "dojo/_base/array", "umc/tools", - "umc/widgets/Form", - "umc/widgets/Page", - "umc/widgets/MultiSelect", + "umc/widgets/StandbyMixin", + "umc/modules/appcenter/AppCenterPage", "umc/i18n!umc/modules/setup" -], function(declare, lang, array, tools, Form, Page, MultiSelect, _) { +], function(declare, lang, array, tools, StandbyMixin, AppCenterPage, _) { - return declare("umc.modules.setup.SoftwarePage", [ Page ], { - // summary: - // This class renderes a detail page containing subtabs and form elements - // in order to edit UDM objects. + return declare("umc.modules.setup.SoftwarePage", [ AppCenterPage, StandbyMixin ], { + liveSearch: false, + addMissingAppButton: false, + standbyDuringUpdateApplications: false, - // system-setup-boot - wizard_mode: false, + title: _('Software'), + headerText: _('Software settings'), + helpText: _('Via the software settings, particular software components may be installed or removed.'), - // __systemsetup__ user is logged in at local firefox session - local_mode: false, - - umcpCommand: tools.umcpCommand, - - // internal reference to the formular containing all form widgets of an UDM object - _form: null, - - _orgComponents: undefined, - - _noteShowed: false, - - postMixInProperties: function() { - this.inherited(arguments); - - this.title = _('Software'); - this.headerText = _('Software settings'); - this.helpText = _('Via the software settings, particular software components may be installed or removed.'); + getAppCenterSeen: function() { + return true; }, - buildRendering: function() { - this.inherited(arguments); - - var widgets = [{ - type: MultiSelect, - name: 'components', - label: _('Installed software components'), - umcpCommand: this.umcpCommand, - dynamicValues: 'setup/software/components', - dynamicOptions: {}, - sortDynamicValues: false, - style: 'width: 500px;', - height: '200px' - }]; - - var layout = [{ - label: _('Installation of software components'), - layout: ['components'] - }]; - - this._form = new Form({ - widgets: widgets, - layout: layout, - scrollable: true - }); - this._form.on('submit', lang.hitch(this, 'onSave')); - - this.addChild(this._form); - - // show notes when samba 3/4 is selected - this.own(this._form.getWidget('components').watch('value', lang.hitch(this, function(name, oldVal, newVal) { - array.forEach(['samba', 'samba4'], function(ikey) { - var r = new RegExp('univention-' + ikey + '\\b'); - if (array.some(this._getInstalledComponents(), function(icomponent) { return (r.test(icomponent)); }, this)) { - this._showNote(ikey); - } - }, this); - }))); - - // show notes for changes in the software settings - this.own(this._form.getWidget('components').watch('value', lang.hitch(this, function() { - this._showNote('software'); - }))); - - // remember which notes have already been shown - this._noteShowed = { }; - this._myNotes = { - samba: _('It is not possible to mix NT and Active Directory compatible domaincontroller. Make sure the existing UCS domain is NT-compatible (Samba 3).'), - samba4: _('It is not possible to mix NT and Active Directory compatible domaincontroller. Make sure the existing UCS domain is Active Directory-compatible (Samba 4).'), - software: _('Installing or removing software components may result in restarting or stopping services. This can have severe side-effects when the system is in productive use at the moment.') - }; + showDetails: function(app) { + // do not show anything. toggle installed flag + app.is_installed = !app.is_installed; + this._grid.store.notify(app, app.id); + // TODO: show warning? notes? }, - _showNote: function(key) { - if (!(key in this._noteShowed) || !this._form.getWidget('components').focused) { - // make sure key exists - return; - } - - if (!this._noteShowed[key]) { - this._noteShowed[key] = true; - this.addNote(this._myNotes[key]); - } - }, - setValues: function(vals) { - // set dynamicOption to get list of components corresponding to selected system role - this._form.getWidget('components').set('dynamicOptions', { role: vals['server/role'] }); - - // get a dict of all installed components and initialise component list - var components = (vals.components || '').split(/\s+/); - this._form.getWidget('components').setInitialValue(components, true); - + this._components = lang.clone(vals.components); if (this._orgComponents === undefined) { - this._orgComponents = {}; - array.forEach(components, function(icomponent) { - if(icomponent !== "") { - this._orgComponents[icomponent] = true; - } - }, this); + this._orgComponents = lang.clone(this._components); } + this.updateApplications(); + this.set('appQuery', { + serverrole: { + test: function(serverrole) { + return serverrole.length === 0 || array.indexOf(serverrole, vals['server/role']) >= 0; + } + } + }); + }, - // handling of notes - this._noteShowed = { }; - var role = vals['server/role']; - if (role == 'domaincontroller_backup' || role == 'domaincontroller_slave') { - // only show samba notes on backup/slave - this._noteShowed.samba = false; - this._noteShowed.samba4 = false; - } - // show note when changing software only on a joined system in productive mode - this._noteShowed.software = this.wizard_mode; - this.clearNotes(); + getApplications: function() { + return this._components || []; }, getValues: function() { return { - components: this._form.getWidget('components').get('value').join(' ') + components: this.getApplications() }; }, - _getComponents: function() { - // return a dict of currently selected components - var components = {}; - array.forEach(this._form.get('value').components, function(icomp) { - components[icomp] = true; - }); - return components; - }, - _getRemovedComponents: function() { // if a previously installed component has been deselected // -> uninstall all its packages - var components = []; - var selectedComponents = this._getComponents(); - tools.forIn(this._orgComponents, function(icomponent) { - if (!(icomponent in selectedComponents)) { - components.push(icomponent); - } - }); - return components; + return array.filter(this._orgComponents, lang.hitch(this, function(component) { + return component.is_installed && !this._grid.store.get(component.id).is_installed; + })); }, _getInstalledComponents: function() { // if a previously not/partly installed component has been selected // -> install all its packages - var components = []; - tools.forIn(this._getComponents(), function(icomponent) { - if (!(icomponent in this._orgComponents)) { - components.push(icomponent); - } - }, this); - return components; + return array.filter(this._orgComponents, lang.hitch(this, function(component) { + return !component.is_installed && this._grid.store.get(component.id).is_installed; + })); }, getSummary: function() { // a list of all components with their labels var allComponents = {}; - array.forEach(this._form.getWidget('components').getAllItems(), function(iitem) { - allComponents[iitem.id] = iitem.label; + array.forEach(this._grid.store.data, function(component) { + allComponents[component.id] = component.name; }); // get changed components @@ -218,7 +115,7 @@ var components = []; if (installComponents.length) { components = array.map(installComponents, function(icomponent) { - return allComponents[icomponent]; + return allComponents[icomponent.id]; }); result.push({ variables: ['components'], @@ -228,7 +125,7 @@ } if (removeComponents.length) { components = array.map(removeComponents, function(icomponent) { - return allComponents[icomponent]; + return allComponents[icomponent.id]; }); result.push({ variables: ['components'], Index: umc/js/setup.js =================================================================== --- umc/js/setup.js (Revision 43637) +++ umc/js/setup.js (Arbeitskopie) @@ -120,7 +120,7 @@ }, renderPages: function(ucr, values) { - this._progressBar = new ProgressBar(); + this._progressBar = new ProgressBar({}); this.own(this._progressBar); this.standby(true); Index: usr/lib/univention-system-setup/scripts/50_software/20apps =================================================================== --- usr/lib/univention-system-setup/scripts/50_software/20apps (Revision 0) +++ usr/lib/univention-system-setup/scripts/50_software/20apps (Revision 0) @@ -0,0 +1,94 @@ +#!/usr/bin/python2.6 +# -*- coding: utf-8 -*- +# +# Univention System Setup +# apps installation script +# +# Copyright 2013 Univention GmbH +# +# http://www.univention.de/ +# +# All rights reserved. +# +# The source code of this program is made available +# under the terms of the GNU Affero General Public License version 3 +# (GNU AGPL V3) as published by the Free Software Foundation. +# +# Binary versions of this program provided by Univention to you as +# well as other copyrighted, protected or trademarked materials like +# Logos, graphics, fonts, specific documentations and configurations, +# cryptographic keys etc. are subject to a license agreement between +# you and Univention and not subject to the GNU AGPL V3. +# +# In the case you use this program under the terms of the GNU AGPL V3, +# the program is provided 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public +# License with the Debian GNU/Linux or Univention distribution in file +# /usr/share/common-licenses/AGPL-3; if not, see +# . +import os.path + +from univention.management.console.modules.setup.setup_script import AptScript, main, _ +from univention.management.console.modules.appcenter.app_center import Application +from univention.updater import UniventionUpdater +from univention.management.console.modules.appcenter.util import ComponentManager +from univention.config_registry import ConfigRegistry + +class AppScript(AptScript): + name = _('Configuring software components') + script_name = os.path.abspath(__file__) + brutal_apt_options = False + + def up(self): + super(AppScript, self).up() + + # get this scripts configuration options + self.apps_remove = self.get_profile_var_list('apps_remove') + self.apps_install = self.get_profile_var_list('apps_install') + ucr = ConfigRegistry() + uu = UniventionUpdater(False) + self.component_manager = ComponentManager(ucr, uu) + + def inner_run(self): + if not (self.apps_remove or self.apps_install): + # nothing to do. dont autoremove either + return True + # +1 for update + ntasks = len(self.apps_remove) + len(self.apps_install) + 1 + self.steps(ntasks * 100) + + # apt-get update + if self.update(): + self.finish_task('Update') + else: + return False + base_package = self.get_package_for_role(self.current_server_role) + if base_package is None: + return False + else: + self.set_always_install(base_package) + + Application._get_category_translations(fake=True) + Application.all(only_local=True) + for app_id in self.apps_remove: + # apt-get remove *app.defaultpackages + app = Application.find(app_id) + app.uninstall(self.package_manager, self.component_manager) + self.finish_task(app.name) + + for app_id in self.apps_install: + # apt-get install *app.defaultpackages + app = Application.find(app_id) + app.install(self.package_manager, self.component_manager) + self.finish_task(app.name) + + return True + +if __name__ == '__main__': + script = AppScript() + main(script) + Index: debian/control =================================================================== --- debian/control (Revision 43637) +++ debian/control (Arbeitskopie) @@ -22,6 +22,7 @@ shell-univention-lib, python-univention-lib (>= 1.0.25-1), console-tools, + univention-management-console-module-appcenter, shell-univention-lib (>= 3.0.1-1), python-univention-lib (>= 3.0.1-1) Recommends: univention-management-console-module-setup