def _remote_appcenter(self, host, function=None): if host is None: raise ValueError('Cannot connect to None') if not host.endswith('.%s' % self.ucr.get('domainname')): raise ValueError('Only connect to FQDNs within the domain') info = get_action('info') opts = {'version': info.get_ucs_version()} if function is not None: opts['function'] = function try: client = Client(host, self.username, self.password) response = client.umc_command('appcenter/version2', opts) except (HTTPError) as exc: raise umcm.UMC_Error( _('Problems connecting to {0} ({1}). Please update {0}!'). format(host, exc.message)) except (ConnectionError, Exception) as exc: raise umcm.UMC_Error( _('Problems connecting to {} ({}).').format(host, str(exc))) err_msg = _( 'The App Center version of the this host ({}) is not compatible with the version of {} ({})' ).format(opts['version'], host, response.result.get('version')) # i guess this is kind of bad if response.status != 200: raise umcm.UMC_Error(err_msg) # remote says he is not compatible if response.result.get('compatible', True) is False: raise umcm.UMC_Error(err_msg) # i'm not compatible if not info.is_compatible(response.result.get('version')): raise umcm.UMC_Error(err_msg) return client
def _install_master_packages_on_host(self, app, function, host): client = Client(host, self.username, self.password) result = client.umc_command('appcenter/invoke', {'function': function, 'application': app.id, 'force': True, 'dont_remote_install': True}).result if result['can_continue']: all_errors = self._query_remote_progress(client) return len(all_errors) == 0 else: MODULE.warn('%r' % result) return False
def _check_remote_host(app_id, host, host_is_master, username, password, force, remote_function): MODULE.process('Starting dry_run for %s on %s' % (app_id, host)) MODULE.process('%s: Connecting...' % host) try: client = Client(host, username, password) except (ConnectionError, HTTPError) as exc: MODULE.warn('_check_remote_host: %s: %s' % (host, exc)) unreachable.append(host) if host_is_master: remote_info['master_unreachable'] = True else: MODULE.process('%s: ... done' % host) host_info = {} MODULE.process('%s: Getting version...' % host) try: host_version = client.umc_command('appcenter/version', {'version': info.get_compatibility()}).result except Forbidden: # command is not yet known (older app center) MODULE.process('%s: ... forbidden!' % host) host_version = None except (ConnectionError, HTTPError) as exc: MODULE.warn('%s: Could not get appcenter/version: %s' % (exc,)) raise except Exception as exc: MODULE.error('%s: Exception: %s' % (host, exc)) raise MODULE.process('%s: ... done' % host) host_info['compatible_version'] = info.is_compatible(host_version) MODULE.process('%s: Invoking %s ...' % (host, remote_function)) try: host_info['result'] = client.umc_command('appcenter/invoke_dry_run', { 'function': remote_function, 'application': app_id, 'force': force, 'dont_remote_install': True, }).result except Forbidden: # command is not yet known (older app center) MODULE.process('%s: ... forbidden!' % host) host_info['result'] = {'can_continue': False, 'serious_problems': False} except (ConnectionError, HTTPError) as exc: MODULE.warn('Could not get appcenter/version: %s' % (exc,)) raise MODULE.process('%s: ... done' % host) if not host_info['compatible_version'] or not host_info['result']['can_continue']: remote_info['problems_with_hosts'] = True if host_info['result']['serious_problems'] or not host_info['compatible_version']: remote_info['serious_problems_with_hosts'] = True hosts_info[host] = host_info MODULE.process('Finished dry_run for %s on %s' % (app_id, host))
def join_ad(): """ Function for joining an AD domain, mimicking a join from umc""" global client client = Client(options.host, options.username, options.password, language='en-US') if options.sync_mode: # join in sync mode: print '=== AD-JOIN SYNC MODE SELECTED ===' join_sync_mode() else: # join in read mode: print '=== AD-JOIN READ MODE SELECTED ===' join_read_mode()
def _decorator(self, request, *args, **kwargs): if not IS_SELFSERVICE_MASTER: try: language = str(self.locale).split('.')[0].replace('_', '-') client = Client(SELFSERVICE_MASTER, language=language) client.authenticate_with_machine_account() response = client.umc_command(request.arguments[0], request.options) except (Unauthorized, ConnectionError) as exc: raise UMC_Error(_('The connection to the server could not be established. Please try again later. Error message was: %s') % (exc,), status=503) except HTTPError as exc: response = exc.response self.finished(request.id, response.result, message=response.message, status=response.status) return return func(self, request, *args, **kwargs)
try: result = client.umc_command(path).result except univention.lib.umc.ConnectionError: print('... Apache down? Ignoring...') continue print(result) if result.get('finished', False): break else: raise Exception("wait timeout") print(result) assert not result['errors'] client = Client(options.host, options.username, options.password, language='en-US') request_options = { "ip": options.domain_host, "username": options.domain_admin, "password": options.domain_password } print('starting connect') response = client.umc_command("adtakeover/connect", request_options) print(response.result) assert response.status == 200 try: print('starting copy') response = client.umc_command("adtakeover/run/copy", request_options)
def __init__(self, host, username=None, password=None, error_handler=None): # type: (str, Optional[str], Optional[str], Optional[Callable[[str], None]]) -> None self.client = Client(host, username, password) self._error_handler = error_handler self.build_data = self.client._Client__build_data
def invoke(self, request): # ATTENTION!!!!!!! # this function has to stay compatible with the very first App Center installations (Dec 2012) # if you add new arguments that change the behavior # you should add a new method (see invoke_dry_run) or add a function name (e.g. install-schema) # this is necessary because newer app center may talk remotely with older one # that does not understand new arguments and behaves the old way (in case of # dry_run: install application although they were asked to dry_run) host = request.options.get('host') function = request.options.get('function') send_as = function if function.startswith('install'): function = 'install' if function.startswith('update'): function = 'upgrade' if function == 'uninstall': function = 'remove' app_id = request.options.get('application') app = Apps().find(app_id) if app is None: raise umcm.UMC_Error(_('Could not find an application for %s') % (app_id,)) force = request.options.get('force') values = request.options.get('values') only_dry_run = request.options.get('only_dry_run') dont_remote_install = request.options.get('dont_remote_install') only_master_packages = send_as.endswith('schema') MODULE.process('Try to %s (%s) %s on %s. Force? %r. Only master packages? %r. Prevent installation on other systems? %r. Only dry run? %r.' % (function, send_as, app_id, host, force, only_master_packages, dont_remote_install, only_dry_run)) # REMOTE invocation! if host and host != self.ucr.get('hostname'): try: client = Client(host, self.username, self.password) result = client.umc_command('appcenter/invoke', request.options).result except (ConnectionError, HTTPError) as exc: MODULE.error('Error during remote appcenter/invoke: %s' % (exc,)) result = { 'unreachable': [host], 'master_unreachable': True, 'serious_problems': True, 'software_changes_computed': True, # not really... } else: if result['can_continue']: def _thread_remote(_client): with self.is_working(): self._query_remote_progress(_client) def _finished_remote(thread, result): if isinstance(result, BaseException): MODULE.warn('Exception during %s %s: %s' % (function, app_id, str(result))) thread = notifier.threads.Simple('invoke', notifier.Callback(_thread_remote, client), _finished_remote) thread.run() self.finished(request.id, result) return # make sure that the application can be installed/updated action = get_action(function)() args = action._build_namespace(app=app, username=self.username, password=self.password, noninteractive=True, skip_checks=['shall_have_enough_ram', 'shall_only_be_installed_in_ad_env_with_password_service', 'must_not_have_concurrent_operation'], send_info=not only_master_packages, set_vars=values, dry_run=True, install_master_packages_remotely=False, only_master_packages=only_master_packages) can_continue = True delayed_can_continue = True serious_problems = False result = { 'install': [], 'remove': [], 'broken': [], 'unreachable': [], 'master_unreachable': False, 'serious_problems': False, 'hosts_info': {}, 'problems_with_hosts': False, 'serious_problems_with_hosts': False, 'invokation_forbidden_details': {}, 'invokation_warning_details': {}, 'software_changes_computed': False, } if not app: MODULE.process('Application not found: %s' % app_id) can_continue = False if can_continue and not only_master_packages: if function == 'upgrade': app = Apps().find_candidate(app) if app is None: forbidden, warnings = {'must_have_candidate': False}, {} else: forbidden, warnings = app.check(function) if forbidden: MODULE.process('Cannot %s %s: %r' % (function, app_id, forbidden)) result['invokation_forbidden_details'] = forbidden can_continue = False serious_problems = True if warnings: MODULE.process('Warning trying to %s %s: %r' % (function, app_id, forbidden)) result['invokation_warning_details'] = warnings if not force: # don't stop "immediately". # compute the package changes! delayed_can_continue = False result['serious_problems'] = serious_problems result['can_continue'] = can_continue if can_continue: with self.locked(): if can_continue and function in ('install', 'upgrade'): result.update(self._install_dry_run_remote(app, function, dont_remote_install, force)) serious_problems = bool(result['master_unreachable'] or result['serious_problems_with_hosts']) if serious_problems: args.dry_run = True result.update(action.dry_run(app, args)) result['software_changes_computed'] = True serious_problems = bool(result['broken'] or serious_problems) if serious_problems or (not force and (result['unreachable'] or result['install'] or result['remove'] or result['problems_with_hosts'])): can_continue = False elif can_continue and function in ('remove',) and not force: result.update(action.dry_run(app, args)) result['software_changes_computed'] = True can_continue = False can_continue = can_continue and delayed_can_continue and not only_dry_run result['serious_problems'] = serious_problems result['can_continue'] = can_continue if can_continue and not only_dry_run: def _thread(module, app, function): with module.is_working(): if not dont_remote_install and function != 'remove': self._install_master_packages_on_hosts(app, function) with module.package_manager.no_umc_restart(exclude_apache=True): try: args.dry_run = False args.install_master_packages_remotely = False return action.call_with_namespace(args) except AppCenterError as exc: raise umcm.UMC_Error(str(exc), result=dict( display_feedback=True, title='%s %s' % (exc.title, exc.info))) def _finished(thread, result): if isinstance(result, BaseException): MODULE.warn('Exception during %s %s: %s' % (function, app_id, str(result))) thread = notifier.threads.Simple('invoke', notifier.Callback(_thread, self, app, function), _finished) thread.run() self.finished(request.id, result)
def invoke_remote_docker(self, host, function, app, force, values, progress): options = {'function': function, 'app': app, 'force': force, 'values': values} client = Client(host, self.username, self.password) result = client.umc_command('appcenter/docker/invoke', options).result self._remote_progress[progress.id] = client, result['id']
def __init__(self, host, username=None, password=None, error_handler=None): self.client = Client(host, username, password) self._error_handler = error_handler self.build_data = self.client._Client__build_data
def umc(username, password, master, path, options=None, flavor=None): MODULE.info('Executing on %r as %r: %r flavor=%r options=%r' % (master, username, path, flavor, options)) client = Client(master, username, password) return client.umc_command(path, options, flavor)
def _thread(): # perform all actions inside a thread... # collect files progress.component(_('Collecting exam files...')) if project: project.collect() progress.add_steps(10) # open a new connection to the master UMC master = ucr['ldap/master'] try: client = Client(master) client.authenticate_with_machine_account() except (ConnectionError, HTTPError) as exc: MODULE.error('Could not connect to UMC on %s: %s' % (master, exc)) raise UMC_Error( _('Could not connect to master server %s.') % (master, )) school = SchoolSearchBase.getOU(request.options['room']) # unset exam mode for the given computer room progress.component(_('Configuring the computer room...')) client.umc_command( 'schoolexam-master/unset-computerroom-exammode', dict( roomdn=request.options['room'], school=school, )).result progress.add_steps(5) # delete exam users accounts if project: # get a list of user accounts in parallel exams parallelUsers = dict([ (iuser.username, iproject.description) for iproject in util.distribution.Project.list() if iproject.name != project.name for iuser in iproject.recipients ]) progress.component(_('Removing exam accounts')) percentPerUser = 25.0 / (1 + len(project.recipients)) for iuser in project.recipients: progress.info( '%s, %s (%s)' % (iuser.lastname, iuser.firstname, iuser.username)) try: if iuser.username not in parallelUsers: # remove first the home directory shutil.rmtree(iuser.unixhome, ignore_errors=True) # remove LDAP user entry client.umc_command( 'schoolexam-master/remove-exam-user', dict( userdn=iuser.dn, school=school, )).result MODULE.info('Exam user has been removed: %s' % iuser.dn) else: MODULE.process( 'Cannot remove the user account %s as it is registered for the running exam "%s", as well' % (iuser.dn, parallelUsers[iuser.username])) except (ConnectionError, HTTPError) as e: MODULE.warn( 'Could not remove exam user account %s: %s' % (iuser.dn, e)) # indicate the user has been processed progress.add_steps(percentPerUser) progress.add_steps(percentPerUser)
def _thread(): # make sure that a project with the same name does not exist directory = request.options['directory'] # get absolute path of project file and test for existance fn_test_project = util.distribution.Project.sanitize_project_filename( directory) if os.path.exists(fn_test_project): raise UMC_Error( _('An exam with the name "%s" already exists. Please choose a different name for the exam.' ) % (directory, )) # validate the project data and save project my.project = util.distribution.Project( dict( name=directory, description=request.options['name'], files=request.options.get('files'), sender=sender, )) my.project.validate() my.project.save() # copy files into project directory if self._tmpDir: for ifile in my.project.files: isrc = os.path.join(self._tmpDir, ifile) itarget = os.path.join(my.project.cachedir, ifile) if os.path.exists(isrc): # copy file to cachedir shutil.move(isrc, itarget) os.chown(itarget, 0, 0) # open a new connection to the master UMC try: master = ucr['ldap/master'] client = Client(master) client.authenticate_with_machine_account() except (ConnectionError, HTTPError) as exc: MODULE.error('Could not connect to UMC on %s: %s' % (master, exc)) raise UMC_Error( _('Could not connect to master server %s.') % ucr.get('ldap/master')) # mark the computer room for exam mode progress.component( _('Preparing the computer room for exam mode...')) client.umc_command( 'schoolexam-master/set-computerroom-exammode', dict( school=request.options['school'], roomdn=request.options['room'], )).result # FIXME: no error handling progress.add_steps(5) # read all recipients and fetch all user objects users = [] for idn in request.options['recipients']: ientry = util.distribution.openRecipients(idn, ldap_user_read) if not ientry: continue # recipients can in theory be users or groups members = [] if isinstance(ientry, util.distribution.User): members = [ientry] elif isinstance(ientry, util.distribution.Group): members = ientry.members for entry in members: # ignore exam users user = User.from_dn(entry.dn, None, ldap_user_read) if not user.is_exam_student(ldap_user_read): users.append(entry) # start to create exam user accounts progress.component(_('Preparing exam accounts')) percentPerUser = 25.0 / (1 + len(users)) examUsers = set() student_dns = set() usersReplicated = set() for iuser in users: progress.info( '%s, %s (%s)' % (iuser.lastname, iuser.firstname, iuser.username)) try: ires = client.umc_command( 'schoolexam-master/create-exam-user', dict( school=request.options['school'], userdn=iuser.dn, )).result examuser_dn = ires.get('examuserdn') examUsers.add(examuser_dn) student_dns.add(iuser.dn) MODULE.info('Exam user has been created: %r' % examuser_dn) except (ConnectionError, HTTPError) as exc: MODULE.warn( 'Could not create exam user account for %r: %s' % (iuser.dn, exc)) # indicate the the user has been processed progress.add_steps(percentPerUser) client.umc_command( 'schoolexam-master/add-exam-users-to-groups', dict( users=list(student_dns), school=request.options['school'], )) progress.add_steps(percentPerUser) # wait for the replication of all users to be finished progress.component(_('Preparing user home directories')) recipients = [] # list of User objects for all exam users openAttempts = 30 * 60 # wait max. 30 minutes for replication while (len(examUsers) > len(usersReplicated)) and (openAttempts > 0): openAttempts -= 1 MODULE.info( 'waiting for replication to be finished, %s user objects missing' % (len(examUsers) - len(usersReplicated))) for idn in examUsers - usersReplicated: try: ldap_user_read.get(idn, required=True) except ldap.NO_SUCH_OBJECT: continue # not replicated yet iuser = util.distribution.openRecipients( idn, ldap_user_read) if not iuser: continue # not a users/user object MODULE.info('user has been replicated: %s' % idn) # call hook scripts if 0 != subprocess.call([ '/bin/run-parts', CREATE_USER_POST_HOOK_DIR, '--arg', iuser.username, '--arg', iuser.dn, '--arg', iuser.homedir ]): raise ValueError( 'failed to run hook scripts for user %r' % (iuser.username)) # store User object in list of final recipients recipients.append(iuser) # mark the user as replicated usersReplicated.add(idn) progress.info( '%s, %s (%s)' % (iuser.lastname, iuser.firstname, iuser.username)) progress.add_steps(percentPerUser) # wait a second time.sleep(1) progress.add_steps(percentPerUser) if openAttempts <= 0: MODULE.error( 'replication timeout - %s user objects missing: %r ' % ((len(examUsers) - len(usersReplicated)), (examUsers - usersReplicated))) raise UMC_Error( _('Replication timeout: could not create all exam users')) # update the final list of recipients my.project.recipients = recipients my.project.save() # update local NSS group cache if ucr.is_true('nss/group/cachefile', True): cmd = ['/usr/lib/univention-pam/ldap-group-to-file.py'] if ucr.is_true('nss/group/cachefile/check_member', False): cmd.append('--check_member') MODULE.info('Updating local nss group cache...') if subprocess.call(cmd): MODULE.error('Updating local nss group cache failed: %s' % ' '.join(cmd)) else: MODULE.info( 'Update of local nss group cache finished successfully.' ) # distribute exam files progress.component(_('Distributing exam files')) progress.info('') my.project.distribute() progress.add_steps(20) # prepare room settings via UMCP... # first step: acquire room # second step: adjust room settings progress.component(_('Prepare room settings')) try: user_client = Client(None, self.username, self.password) except (ConnectionError, HTTPError) as exc: MODULE.warn('Authentication failed: %s' % (exc, )) raise UMC_Error(_('Could not connect to local UMC server.')) room = request.options['room'] MODULE.info('Acquire room: %s' % (room, )) user_client.umc_command('computerroom/room/acquire', dict( room=request.options['room'], )).result progress.add_steps(1) MODULE.info('Adjust room settings:\n%s' % '\n'.join( [' %s=%s' % (k, v) for k, v in request.options.iteritems()])) user_client.umc_command( 'computerroom/exam/start', dict( room=room, examDescription=request.options['name'], exam=directory, examEndTime=request.options.get('examEndTime'), )).result progress.add_steps(4) user_client.umc_command( 'computerroom/settings/set', dict( room=room, internetRule=request.options['internetRule'], customRule=request.options.get('customRule'), shareMode=request.options['shareMode'], printMode='default', )).result progress.add_steps(5)
def invoke(self, request): # ATTENTION!!!!!!! # this function has to stay compatible with the very first App Center installations (Dec 2012) # if you add new arguments that change the behaviour # you should add a new method (see invoke_dry_run) or add a function name (e.g. install-schema) # this is necessary because newer app center may talk remotely with older one # that does not understand new arguments and behaves the old way (in case of # dry_run: install application although they were asked to dry_run) host = request.options.get('host') function = request.options.get('function') send_as = function if function.startswith('install'): function = 'install' if function.startswith('update'): function = 'update' application_id = request.options.get('application') Application.all(only_local=True) # if not yet cached, cache. but use only local inis application = Application.find(application_id) if application is None: raise umcm.UMC_Error(_('Could not find an application for %s') % (application_id,)) force = request.options.get('force') only_dry_run = request.options.get('only_dry_run') dont_remote_install = request.options.get('dont_remote_install') only_master_packages = send_as.endswith('schema') MODULE.process('Try to %s (%s) %s on %s. Force? %r. Only master packages? %r. Prevent installation on other systems? %r. Only dry run? %r.' % (function, send_as, application_id, host, force, only_master_packages, dont_remote_install, only_dry_run)) # REMOTE invocation! if host and host != self.ucr.get('hostname'): try: client = Client(host, self.username, self.password) result = client.umc_command('appcenter/invoke', request.options).result except (ConnectionError, HTTPError) as exc: MODULE.error('Error during remote appcenter/invoke: %s' % (exc,)) result = { 'unreachable': [host], 'master_unreachable': True, 'serious_problems': True, 'software_changes_computed': True, # not really... } else: if result['can_continue']: def _thread_remote(_client, _package_manager): with _package_manager.locked(reset_status=True, set_finished=True): _package_manager.unlock() # not really locked locally, but busy, so "with locked()" is appropriate Application._query_remote_progress(_client, _package_manager) def _finished_remote(thread, result): if isinstance(result, BaseException): MODULE.warn('Exception during %s %s: %s' % (function, application_id, str(result))) thread = notifier.threads.Simple('invoke', notifier.Callback(_thread_remote, client, self.package_manager), _finished_remote) thread.run() self.finished(request.id, result) return # make sure that the application can be installed/updated can_continue = True delayed_can_continue = True serious_problems = False result = { 'install': [], 'remove': [], 'broken': [], 'unreachable': [], 'master_unreachable': False, 'serious_problems': False, 'hosts_info': {}, 'problems_with_hosts': False, 'serious_problems_with_hosts': False, 'invokation_forbidden_details': {}, 'invokation_warning_details': {}, 'software_changes_computed': False, } if not application: MODULE.process('Application not found: %s' % application_id) can_continue = False if can_continue and not only_master_packages: forbidden, warnings = application.check_invokation(function, self.package_manager) if forbidden: MODULE.process('Cannot %s %s: %r' % (function, application_id, forbidden)) result['invokation_forbidden_details'] = forbidden can_continue = False serious_problems = True if warnings: MODULE.process('Warning trying to %s %s: %r' % (function, application_id, forbidden)) result['invokation_warning_details'] = warnings if not force: # dont stop "immediately". # compute the package changes! delayed_can_continue = False result['serious_problems'] = serious_problems result['can_continue'] = can_continue try: if can_continue: if self._working(): # make it multi-tab safe (same session many buttons to be clicked) raise LockError() with self.package_manager.locked(reset_status=True): previously_registered_by_dry_run = False if can_continue and function in ('install', 'update'): remove_component = only_dry_run dry_run_result, previously_registered_by_dry_run = application.install_dry_run(self.package_manager, self.component_manager, remove_component=remove_component, username=self._username, password=self.password, only_master_packages=only_master_packages, dont_remote_install=dont_remote_install, function=function, force=force) result.update(dry_run_result) result['software_changes_computed'] = True serious_problems = bool(result['broken'] or result['master_unreachable'] or result['serious_problems_with_hosts']) if serious_problems or (not force and (result['unreachable'] or result['install'] or result['remove'] or result['problems_with_hosts'])): MODULE.process('Problems encountered or confirmation required. Removing component %s' % application.component_id) if not remove_component: # component was not removed automatically after dry_run if application.candidate: # operation on candidate failed. re-register original application application.register(self.component_manager, self.package_manager) else: # operation on self failed. unregister all application.unregister_all_and_register(None, self.component_manager, self.package_manager) can_continue = False elif can_continue and function in ('uninstall',) and not force: result['remove'] = application.uninstall_dry_run(self.package_manager) result['software_changes_computed'] = True can_continue = False can_continue = can_continue and delayed_can_continue and not only_dry_run result['serious_problems'] = serious_problems result['can_continue'] = can_continue if can_continue and not only_dry_run: def _thread(module, application, function): with module.package_manager.locked(set_finished=True): with module.package_manager.no_umc_restart(exclude_apache=True): if function in ('install', 'update'): # dont have to add component: already added during dry_run return application.install(module.package_manager, module.component_manager, add_component=only_master_packages, send_as=send_as, username=self._username, password=self.password, only_master_packages=only_master_packages, dont_remote_install=dont_remote_install, previously_registered_by_dry_run=previously_registered_by_dry_run) else: return application.uninstall(module.package_manager, module.component_manager, self._username, self.password) def _finished(thread, result): if isinstance(result, BaseException): MODULE.warn('Exception during %s %s: %s' % (function, application_id, str(result))) thread = notifier.threads.Simple('invoke', notifier.Callback(_thread, self, application, function), _finished) thread.run() else: self.package_manager.set_finished() # nothing to do, ready to take new commands self.finished(request.id, result) except LockError: # make it thread safe: another process started a package manager # this module instance already has a running package manager raise umcm.UMC_Error(_('Another package operation is in progress'))