def stop(self, taskid, stop_type, msg): """ Cancels the given job. Note that when cancelling some part of a job (for example, by passing *taskid* starting with ``R:`` to indicate a particular recipe within a job) the entire job is cancelled. :param taskid: see above :type taskid: string :param stop_type: must be ``'cancel'`` (other values are reserved for Beaker's internal use) :type stop_type: string :param msg: reason for cancelling :type msg: string """ task_type, task_id = taskid.split(":") if task_type.upper() in self.stoppable_task_types.keys(): try: task = self.stoppable_task_types[task_type.upper()].by_id(int(task_id)) except (InvalidRequestError, ValueError): raise BX(_("Invalid %s %s" % (task_type, task_id))) else: raise BX(_("Task type %s is not stoppable" % (task_type))) if stop_type not in task.stop_types: raise BX(_('Invalid stop_type: %s, must be one of %s' % (stop_type, task.stop_types))) if not task.can_stop(identity.current.user): raise BX(_("You don't have permission to %s %s" % (stop_type, taskid))) kwargs = dict(msg = msg) task.record_activity(user=identity.current.user, service=u'XMLRPC', field=u'Status', action=u'Cancelled', old='', new='') try: return getattr(task, stop_type)(**kwargs) except StaleTaskStatusException: raise BX(_(u"Could not cancel job id %s. Please try later" % task_id))
def set(self, value, valid_from=None, user=None): if user is None: try: user = identity.current.user except AttributeError: raise BX(_('Settings may not be changed anonymously')) if valid_from: if valid_from < datetime.utcnow(): raise BX(_('%s is in the past') % valid_from) self.value_class(self, value, user, valid_from)
def create(self, kw): """ Creates a new group. The *kw* argument must be an XML-RPC structure (dict) specifying the following keys: 'group_name' Group name (maximum 16 characters) 'display_name' Group display name 'description' Group description 'ldap' Populate users from LDAP (True/False) Returns a message whether the group was successfully created or raises an exception on failure. """ display_name = kw.get('display_name') group_name = kw.get('group_name') description = kw.get('description') ldap = kw.get('ldap') password = kw.get('root_password') if ldap and not identity.current.user.is_admin(): raise BX(_(u'Only admins can create LDAP groups')) if ldap and not config.get("identity.ldap.enabled", False): raise BX(_(u'LDAP is not enabled')) try: group = Group.by_name(group_name) except NoResultFound: group = Group() session.add(group) group.record_activity(user=identity.current.user, service=u'XMLRPC', field=u'Group', action=u'Created') group.display_name = display_name group.group_name = group_name group.description = description group.root_password = password if ldap: group.membership_type = GroupMembershipType.ldap group.refresh_ldap_members() else: group.add_member(identity.current.user, is_owner=True, service=u'XMLRPC', agent=identity.current.user) return 'Group created: %s.' % group_name else: raise BX(_(u'Group already exists: %s.' % group_name))
def members(self, group_name): """ List the members of an existing group. :param group_name: An existing group name :type group_name: string Returns a list of the members (a dictionary containing each member's username, email, and whether the member is an owner or not). """ try: group = Group.by_name(group_name) except NoResultFound: raise BX(_(u'Group does not exist: %s.' % group_name)) users = [] for u in group.users: user = {} user['username'] = u.user_name user['email'] = u.email_address if group.has_owner(u): user['owner'] = True else: user['owner'] = False users.append(user) return users
def to_xml(self, taskid, clone=False, exclude_enclosing_job=True, include_logs=True): """ Returns an XML representation of the given job component, including its current state. :param taskid: see above :type taskid: string :param clone: If True, returns XML suitable for submitting back to Beaker. Otherwise, the XML includes results. :type clone: bool :param exclude_enclosing_job: If False, returns <job> as the root element even when requesting a recipe set or recipe. This is useful when cloning, in order to always produce a complete job definition. :type exclude_enclosing_job: bool :param include_logs: If True (the default), the results XML includes links to all logs. This can make the results XML substantially larger. :type include_logs: bool """ task_type, task_id = taskid.split(":") if task_type.upper() in self.task_types.keys(): try: task = self.task_types[task_type.upper()].by_id(task_id) except InvalidRequestError: raise BX(_("Invalid %s %s" % (task_type, task_id))) return lxml.etree.tostring( task.to_xml(clone=clone, include_enclosing_job=not exclude_enclosing_job, include_logs=include_logs), xml_declaration=False, encoding='UTF-8')
def set_name(self, user, service, group_name): """ Set a group's name and record any change as group activity """ old_group_name = self.group_name if group_name != old_group_name: if self.is_protected_group(): raise BX(_(u'Cannot rename protected group %r as %r' % (old_group_name, group_name))) self.group_name = group_name self.record_activity(user=user, service=service, field=u'Name', old=old_group_name, new=group_name)
def set_name(self, user, service, group_name): """Set a group's name and record any change as group activity Passing None or the empty string means "leave this value unchanged" """ old_group_name = self.group_name if group_name and group_name != old_group_name: if self.is_protected_group(): raise BX(_(u'Cannot rename protected group %r as %r' % (old_group_name, group_name))) self.group_name = group_name self.record_activity(user=user, service=service, field=u'Name', old=old_group_name, new=group_name)
def to_xml(self, taskid, clone=False, from_job=True): """ Returns an XML representation of the given job component, including its current state. :param taskid: see above :type taskid: string """ task_type, task_id = taskid.split(":") if task_type.upper() in self.task_types.keys(): try: task = self.task_types[task_type.upper()].by_id(task_id) except InvalidRequestError: raise BX(_("Invalid %s %s" % (task_type, task_id))) return task.to_xml(clone, from_job).toxml()
def power(self, action, fqdn, clear_netboot=False, force=False, delay=0): """ Controls power for the system with the given fully-qualified domain name. If the *clear_netboot* argument is True, the Cobbler netboot configuration for the system will be cleared before power controlling. Controlling power for a system is not normally permitted when the system is in use by someone else, because it is likely to interfere with their usage. Callers may pass True for the *force* argument to override this safety check. This method does not wait for Cobbler to report whether the power control was succesful. :param action: 'on', 'off', or 'reboot' :type action: string :param fqdn: fully-qualified domain name of the system to be power controlled :type fqdn: string :param clear_netboot: whether to clear netboot configuration before powering :type clear_netboot: boolean :param force: whether to power the system even if it is in use :type force: boolean :param delay: number of seconds to delay before performing the action (default none) :type delay: int or float .. versionadded:: 0.6 .. versionchanged:: 0.6.14 No longer waits for completion of Cobbler power task. """ system = System.by_fqdn(fqdn, identity.current.user) if not system.can_power(identity.current.user): raise InsufficientSystemPermissions( _(u'User %s does not have permission to power system %s') % (identity.current.user, system)) if not force and system.user is not None \ and system.user != identity.current.user: raise BX(_(u'System is in use')) if clear_netboot: system.clear_netboot(service=u'XMLRPC') system.action_power(action, service=u'XMLRPC', delay=delay) return system.fqdn # because turbogears makes us return something
def create_from_taskinfo(cls, raw_taskinfo): """Create a new task object based on details retrieved from an RPM""" tinfo = testinfo.parse_string(raw_taskinfo['desc'].decode('utf8')) if len(tinfo.test_name) > 255: raise BX(_("Task name should be <= 255 characters")) if tinfo.test_name.endswith('/'): raise BX(_(u'Task name must not end with slash')) if '//' in tinfo.test_name: raise BX(_(u'Task name must not contain redundant slashes')) task = cls.lazy_create(name=tinfo.test_name) # RPM is the same version we have. don't process if task.version == raw_taskinfo['hdr']['ver']: raise BX( _("Failed to import, %s is the same version we already have" % task.version)) # if the task is already present, check if a downgrade has been requested if task.version: downgrade = cls.check_downgrade(task.version, raw_taskinfo['hdr']['ver']) else: downgrade = False task.version = raw_taskinfo['hdr']['ver'] task.description = tinfo.test_description task.types = [] task.bugzillas = [] task.required = [] task.runfor = [] task.needs = [] task.excluded_osmajor = [] task.excluded_arch = [] includeFamily = [] for family in tinfo.releases: if family.startswith('-'): try: if family.lstrip('-') not in task.excluded_osmajor: task.excluded_osmajor.append( TaskExcludeOSMajor(osmajor=OSMajor.by_name_alias( family.lstrip('-')))) except InvalidRequestError: pass else: try: includeFamily.append(OSMajor.by_name_alias(family).osmajor) except InvalidRequestError: pass families = set(['%s' % family.osmajor for family in OSMajor.query]) if includeFamily: for family in families.difference(set(includeFamily)): if family not in task.excluded_osmajor: task.excluded_osmajor.append( TaskExcludeOSMajor( osmajor=OSMajor.by_name_alias(family))) if tinfo.test_archs: arches = set(['%s' % arch.arch for arch in Arch.query]) for arch in arches.difference(set(tinfo.test_archs)): if arch not in task.excluded_arch: task.excluded_arch.append( TaskExcludeArch(arch=Arch.by_name(arch))) task.avg_time = tinfo.avg_test_time for type in tinfo.types: ttype = TaskType.lazy_create(type=type) task.types.append(ttype) for bug in tinfo.bugs: task.bugzillas.append(TaskBugzilla(bugzilla_id=bug)) task.path = tinfo.test_path # Bug 772882. Remove duplicate required package here # Avoid ORM insert in task_packages_required_map twice. tinfo.runfor = list(set(tinfo.runfor)) for runfor in tinfo.runfor: package = TaskPackage.lazy_create(package=runfor) task.runfor.append(package) task.priority = tinfo.priority task.destructive = tinfo.destructive # Bug 772882. Remove duplicate required package here # Avoid ORM insert in task_packages_required_map twice. tinfo.requires = list(set(tinfo.requires)) for require in tinfo.requires: package = TaskPackage.lazy_create(package=require) task.required.append(package) for need in tinfo.needs: task.needs.append(TaskPropertyNeeded(property=need)) task.license = tinfo.license task.owner = tinfo.owner try: task.uploader = identity.current.user except identity.RequestRequiredException: task.uploader = User.query.get(1) task.valid = True return task, downgrade
def create_from_taskinfo(cls, raw_taskinfo): """Create a new task object based on details retrieved from an RPM""" tinfo = testinfo.parse_string(raw_taskinfo['desc'].decode('utf8')) if len(tinfo.test_name) > 255: raise BX(_("Task name should be <= 255 characters")) if tinfo.test_name.endswith('/'): raise BX(_(u'Task name must not end with slash')) if '//' in tinfo.test_name: # pylint:disable=unsupported-membership-test raise BX(_(u'Task name must not contain redundant slashes')) existing_task = Task.query.filter(Task.name == tinfo.test_name).first() if existing_task is not None: task = existing_task # RPM is the same version we have. don't process if existing_task.version == raw_taskinfo['hdr']['ver']: raise BX(_("Failed to import, %s is the same version we already have" % task.version)) # if the task is already present, check if a downgrade has been requested downgrade = cls.check_downgrade(task.version, raw_taskinfo['hdr']['ver']) else: task = Task(name=tinfo.test_name) downgrade = False task.version = raw_taskinfo['hdr']['ver'] task.description = tinfo.test_description task.types = [] task.bugzillas = [] task.required = [] task.runfor = [] task.needs = [] task.excluded_osmajors = [] task.exclusive_osmajors = [] task.excluded_arches = [] task.exclusive_arches = [] for family in tinfo.releases: if family.startswith('-'): try: osmajor = OSMajor.by_name_alias(family.lstrip('-')) if osmajor not in task.excluded_osmajors: task.excluded_osmajors.append(osmajor) except NoResultFound: pass else: try: osmajor = OSMajor.by_name_alias(family) if osmajor not in task.exclusive_osmajors: task.exclusive_osmajors.append(osmajor) except NoResultFound: pass for value in tinfo.test_archs: if value.startswith('-'): try: arch = Arch.by_name(value.lstrip('-')) except ValueError: pass else: if arch not in task.excluded_arches: task.excluded_arches.append(arch) else: try: arch = Arch.by_name(value) except ValueError: pass else: if arch not in task.exclusive_arches: task.exclusive_arches.append(arch) task.avg_time = tinfo.avg_test_time for type in tinfo.types: ttype = TaskType.lazy_create(type=type) task.types.append(ttype) for bug in tinfo.bugs: task.bugzillas.append(TaskBugzilla(bugzilla_id=bug)) task.path = tinfo.test_path # Bug 772882. Remove duplicate required package here # Avoid ORM insert in task_packages_required_map twice. tinfo.runfor = list(set(tinfo.runfor)) for runfor in tinfo.runfor: package = TaskPackage.lazy_create(package=runfor) task.runfor.append(package) task.priority = tinfo.priority task.destructive = tinfo.destructive # Bug 772882. Remove duplicate required package here # Avoid ORM insert in task_packages_required_map twice. tinfo.requires = list(set(tinfo.requires)) for require in tinfo.requires: package = TaskPackage.lazy_create(package=require) task.required.append(package) for need in tinfo.needs: task.needs.append(TaskPropertyNeeded(property=need)) task.license = tinfo.license task.owner = tinfo.owner try: task.uploader = identity.current.user except identity.RequestRequiredException: task.uploader = User.query.get(1) task.valid = True return task, downgrade
def modify(self, group_name, kw): """ Modifies an existing group. You must be an owner of a group to modify any details. :param group_name: An existing group name :type group_name: string The *kw* argument must be an XML-RPC structure (dict) specifying the following keys: 'group_name' New group name (maximum 16 characters) 'display_name' New group display name 'add_member' Add user (username) to the group 'remove_member' Remove an existing user (username) from the group 'root_password' Change the root password of this group. Returns a message whether the group was successfully modified or raises an exception on failure. """ # if not called from the bkr group-modify if not kw: raise BX(_('Please specify an attribute to modify.')) try: group = Group.by_name(group_name) except NoResultFound: raise BX(_(u'Group does not exist: %s.' % group_name)) if group.membership_type == GroupMembershipType.ldap: if not identity.current.user.is_admin(): raise BX(_(u'Only admins can modify LDAP groups')) if kw.get('add_member', None) or kw.get('remove_member', None): raise BX(_(u'Cannot edit membership of an LDAP group')) user = identity.current.user if not group.can_edit(user): raise BX(_('You are not an owner of group %s' % group_name)) group_name = kw.get('group_name', None) if group_name: try: Group.by_name(group_name) except NoResultFound: pass else: if group_name != group.group_name: raise BX( _(u'Failed to update group %s: Group name already exists: %s' % (group.group_name, group_name))) group.set_name(user, u'XMLRPC', kw.get('group_name', None)) display_name = kw.get('display_name', None) if display_name: group.set_display_name(user, u'XMLRPC', display_name) root_password = kw.get('root_password', None) if root_password: group.set_root_password(user, u'XMLRPC', root_password) if kw.get('add_member', None): username = kw.get('add_member') user = User.by_user_name(username) if user is None: raise BX(_(u'User does not exist %s' % username)) if user.removed: raise BX( _(u'Cannot add deleted user %s to group' % user.user_name)) if user not in group.users: group.add_member(user, service=u'XMLRPC', agent=identity.current.user) mail.group_membership_notify(user, group, agent=identity.current.user, action='Added') else: raise BX( _(u'User %s is already in group %s' % (username, group.group_name))) if kw.get('remove_member', None): username = kw.get('remove_member') user = User.by_user_name(username) if user is None: raise BX(_(u'User does not exist %s' % username)) if user not in group.users: raise BX( _(u'No user %s in group %s' % (username, group.group_name))) else: if not group.can_remove_member(identity.current.user, user.user_id): raise BX(_(u'Cannot remove member')) groupUsers = group.users for usr in groupUsers: if usr.user_id == user.user_id: group.remove_member(user, service=u'XMLRPC', agent=identity.current.user) removed = user mail.group_membership_notify( user, group, agent=identity.current.user, action='Removed') break #dummy success return value return ['1']
def provision(self, fqdn, distro_tree_id, ks_meta=None, kernel_options=None, kernel_options_post=None, kickstart=None, reboot=True): """ Provisions a system with the given distro tree and options. The *ks_meta*, *kernel_options*, and *kernel_options_post* arguments override the default values configured for the system. For example, if the default kernel options for the system/distro are 'console=ttyS0 ksdevice=eth0', and the caller passes 'ksdevice=eth1' for *kernel_options*, the kernel options used will be 'console=ttyS0 ksdevice=eth1'. :param distro_tree_id: numeric id of distro tree to be provisioned :type distro_tree_id: int :param ks_meta: kickstart options :type ks_meta: str :param kernel_options: kernel options for installation :type kernel_options: str :param kernel_options_post: kernel options for after installation :type kernel_options_post: str :param kickstart: complete kickstart :type kickstart: str :param reboot: whether to reboot the system after applying Cobbler changes :type reboot: bool .. versionadded:: 0.6 .. versionchanged:: 0.6.10 System-specific kickstart/kernel options are now obeyed. .. versionchanged:: 0.9 *distro_install_name* parameter is replaced with *distro_tree_id*. See :meth:`distrotrees.filter`. """ system = System.by_fqdn(fqdn, identity.current.user) if not system.user == identity.current.user: raise BX(_(u'Reserve a system before provisioning')) distro_tree = DistroTree.by_id(distro_tree_id) # sanity check: does the distro tree apply to this system? if not system.compatible_with_distro_tree(distro_tree): raise BX( _(u'Distro tree %s cannot be provisioned on %s') % (distro_tree, system.fqdn)) if not system.lab_controller: raise BX(_(u'System is not attached to a lab controller')) if not distro_tree.url_in_lab(system.lab_controller): raise BX( _(u'Distro tree %s is not available in lab %s') % (distro_tree, system.lab_controller)) if identity.current.user.rootpw_expired: raise BX( _('Your root password has expired, please change or clear it in order to submit jobs.' )) # ensure system-specific defaults are used # (overriden by this method's arguments) options = system.manual_provision_install_options(distro_tree)\ .combined_with(InstallOptions.from_strings( ks_meta or '', kernel_options or '', kernel_options_post or '')) if 'ks' not in options.kernel_options: rendered_kickstart = generate_kickstart(options, distro_tree=distro_tree, system=system, user=identity.current.user, kickstart=kickstart) options.kernel_options['ks'] = rendered_kickstart.link system.configure_netboot(distro_tree, options.kernel_options_str, service=u'XMLRPC') system.record_activity(user=identity.current.user, service=u'XMLRPC', action=u'Provision', field=u'Distro Tree', old=u'', new=u'Success: %s' % distro_tree) if reboot: system.action_power(action='reboot', service=u'XMLRPC') return system.fqdn # because turbogears makes us return something
def create(self, kw): """ Creates a new group. The *kw* argument must be an XML-RPC structure (dict) specifying the following keys: 'group_name' Group name (maximum 16 characters) 'display_name' Group display name 'ldap' Populate users from LDAP (True/False) Returns a message whether the group was successfully created or raises an exception on failure. """ display_name = kw.get('display_name') group_name = kw.get('group_name') ldap = kw.get('ldap') password = kw.get('root_password') if ldap and not identity.current.user.is_admin(): raise BX(_(u'Only admins can create LDAP groups')) try: group = Group.by_name(group_name) except NoResultFound: #validate GroupFormSchema.fields['group_name'].to_python(group_name) GroupFormSchema.fields['display_name'].to_python(display_name) group = Group() session.add(group) group.record_activity(user=identity.current.user, service=u'XMLRPC', field=u'Group', action=u'Created') group.display_name = display_name group.group_name = group_name group.ldap = ldap group.root_password = password user = identity.current.user if not ldap: group.user_group_assocs.append( UserGroup(user=user, is_owner=True)) group.activity.append( GroupActivity(user, service=u'XMLRPC', action=u'Added', field_name=u'User', old_value=None, new_value=user.user_name)) group.activity.append( GroupActivity(user, service=u'XMLRPC', action=u'Added', field_name=u'Owner', old_value=None, new_value=user.user_name)) if group.ldap: group.refresh_ldap_members() return 'Group created: %s.' % group_name else: raise BX(_(u'Group already exists: %s.' % group_name))