def revoke_ownership(group_name): """ Revoke group ownership from an existing group user. :param group_name: Group's name. :queryparam user_name: User's username. """ u = identity.current.user group = _get_group_by_name(group_name, lockmode='update') if not group.can_modify_ownership(identity.current.user): raise Forbidden403('Cannot edit ownership of group %s' % group_name) if 'user_name' not in request.args: raise MethodNotAllowed405 user = _get_user_by_username(request.args['user_name']) if user not in group.users: raise BadRequest400('User is not a member of group %s' % group_name) if group.has_owner(user): if len(group.owners()) == 1 and not u.is_admin(): raise Forbidden403('Cannot remove the only owner') group.revoke_ownership(user, agent=identity.current.user) else: raise Conflict409('User %s is not an owner of group %s' % (user.user_name, group_name)) return '', 204
def delete_keystone_trust(username): """ Deletes the Keystone trust for a user account. :param username: The user's username. """ user = _get_user(username) if not user.can_edit(identity.current.user): raise Forbidden403('Cannot edit Keystone trust of user %s' % username) if not user.openstack_trust_id: raise BadRequest400('No Keystone trust created by user %s' % username) try: manager = dynamic_virt.VirtManager(user) manager.delete_keystone_trust() except ValueError as e: # If we can't create a VirtManager we presume that the trust has been # invalidated by different means. log.debug(e.message) except RuntimeError as e: # Sanity check failed. Because OpenStack is not configured. log.debug(e.message) old_trust_id = user.openstack_trust_id user.openstack_trust_id = None user.record_activity(user=identity.current.user, service=u'HTTP', field=u'OpenStack Trust ID', action=u'Deleted', old=old_trust_id) return '', 204
def remove_system_from_pool(pool_name): """ Remove a system from a system pool :param pool_name: System pool's name. :queryparam fqdn: System's fully-qualified domain name """ if 'fqdn' not in request.args: raise MethodNotAllowed405 fqdn = request.args['fqdn'] system = _get_system_by_FQDN(fqdn) u = identity.current.user pool = _get_pool_by_name(pool_name, lockmode='update') if pool in system.pools: if pool.can_edit(u) or system.can_edit(u): if system.active_access_policy == pool.access_policy: system.active_access_policy = system.custom_access_policy system.record_activity(user=u, service=u'HTTP', field=u'Active Access Policy', action=u'Changed', old = pool.access_policy, new = system.custom_access_policy) system.pools.remove(pool) system.record_activity(user=u, service=u'HTTP', action=u'Removed', field=u'Pool', old=unicode(pool), new=None) system.date_modified = datetime.datetime.utcnow() pool.record_activity(user=u, service=u'HTTP', action=u'Removed', field=u'System', old=unicode(system), new=None) else: raise Forbidden403('You do not have permission to modify system %s' 'or remove systems from pool %s' % (system.fqdn, pool.name)) else: raise BadRequest400('System %s is not in pool %s' % (system.fqdn, pool.name)) return '', 204
def exclude_user(group_name): """ Exclude a user from an inverted group. Then the user will not have the group membership. :param group_name: Group's name. :jsonparam string user_name: User's username. """ u = identity.current.user data = read_json_request(request) group = _get_group_by_name(group_name, lockmode='update') if not group.can_modify_membership(identity.current.user): raise Forbidden403('Cannot edit membership of group %s' % group_name) if group.membership_type == GroupMembershipType.normal: raise NotFound404('Normal group %s do not have excluded users' % group_name) if 'user_name' not in data: raise BadRequest400('User not specified') user = _get_user_by_username(data['user_name']) if user in group.users: if not group.can_exclude_member(u, user.id): raise Forbidden403('Cannot exclude user %s from group %s' % (user, group_name)) with convert_internal_errors(): group.exclude_user(user, agent=identity.current.user) else: raise Conflict409('User %s is already excluded from group %s' % (user.user_name, group_name)) return '', 204
def add_group_membership(group_name): """ Add a user to a group. :param group_name: Group's name. :jsonparam string user_name: User's username. :jsonparam boolean is_owner: If true, the given user will become one of the group owners. """ u = identity.current.user data = read_json_request(request) group = _get_group_by_name(group_name, lockmode='update') if not group.can_modify_membership(identity.current.user): raise Forbidden403('Cannot edit membership of group %s' % group_name) if 'user_name' not in data: raise BadRequest400('User not specified') user = _get_user_by_username(data['user_name']) is_owner = data.get('is_owner', False) if user not in group.users: group.add_member(user, is_owner=is_owner, agent=identity.current.user) mail.group_membership_notify(user, group, agent=u, action='Added') else: raise Conflict409('User %s is already a member of group %s' % (user.user_name, group_name)) return '', 204
def delete_powertype(id): """ Deletes a power type by the given id. :param id: The id of the power type to be deleted. :status 204: Power type successfully deleted. :status 400: Power type is referenced by systems. :status 404: Power type can not be found. """ try: powertype = PowerType.by_id(id) except NoResultFound: raise NotFound404('Power type: %s does not exist' % id) systems_referenced = System.query.join( System.power).filter(Power.power_type == powertype).count() if systems_referenced: raise BadRequest400('Power type %s still referenced by %i systems' % (powertype.name, systems_referenced)) session.delete(powertype) activity = Activity(identity.current.user, u'HTTP', u'Deleted', u'PowerType', powertype.name) session.add(activity) return '', 204
def save_system_access_policy(fqdn): system = _get_system_by_FQDN(fqdn) if not system.can_edit_policy(identity.current.user): raise Forbidden403('Cannot edit system policy') if system.custom_access_policy: policy = system.custom_access_policy else: policy = system.custom_access_policy = SystemAccessPolicy() data = read_json_request(request) # Figure out what is added, what is removed. # Rules are immutable, so if it has an id it is unchanged, # if it has no id it is new. kept_rule_ids = frozenset(r['id'] for r in data['rules'] if 'id' in r) removed = [] for old_rule in policy.rules: if old_rule.id not in kept_rule_ids: removed.append(old_rule) for old_rule in removed: system.record_activity(user=identity.current.user, service=u'HTTP', field=u'Access Policy Rule', action=u'Removed', old=repr(old_rule)) policy.rules.remove(old_rule) for rule in data['rules']: if 'id' not in rule: if rule['user']: user = User.by_user_name(rule['user']) if user is None: raise BadRequest400('No such user %r' % rule['user']) else: user = None try: group = Group.by_name(rule['group']) if rule['group'] else None except NoResultFound: raise BadRequest400('No such group %r' % rule['group']) permission = SystemPermission.from_string(rule['permission']) new_rule = policy.add_rule(user=user, group=group, everybody=rule['everybody'], permission=permission) system.record_activity(user=identity.current.user, service=u'HTTP', field=u'Access Policy Rule', action=u'Added', new=repr(new_rule)) return jsonify(policy.__json__())
def doit(): distro_trees = [] for id in request.form.getlist('distro_tree_id'): try: distro_trees.append(DistroTree.by_id(id)) except NoResultFound: raise BadRequest400('Distro tree %r does not exist' % id) job_details = {} job_details['pick'] = request.form.get('pick') or 'auto' system_choice = 'any system' if job_details['pick'] == 'fqdn': try: job_details['system'] = System.by_fqdn(request.form.get('system'), identity.current.user) system_choice = 'a specific system' except DatabaseLookupError: raise BadRequest400('System %s not found' % request.form.get('system')) elif job_details['pick'] == 'lab': try: job_details['lab'] = LabController.by_name(request.form.get('lab')) system_choice = 'any lab system' except NoResultFound: raise BadRequest400('Lab controller %s not found' % request.form.get('lab')) reservetime = int( request.form.get('reserve_duration') or DEFAULT_RESERVE_SECONDS) if reservetime > MAX_SECONDS_PROVISION: raise BadRequest400( 'Reservation time exceeds maximum time of %s hours' % MAX_HOURS_PROVISION) job_details['reservetime'] = reservetime job_details['whiteboard'] = request.form.get('whiteboard') if not job_details['whiteboard']: job_details['whiteboard'] = ( "Reserve Workflow provision of distro %s on %s for %d seconds" % (request.form.get('distro'), system_choice, job_details['reservetime'])) job_details['ks_meta'] = request.form.get('ks_meta') job_details['koptions'] = request.form.get('koptions') job_details['koptions_post'] = request.form.get('koptions_post') with convert_internal_errors(): job = Job.provision_system_job(distro_trees, **job_details) return 'Created %s' % job.t_id, 201, [('Location', absolute_url('/jobs/%s' % job.id))]
def _extend_watchdog(recipe_id, data): recipe = _get_recipe_by_id(recipe_id) kill_time = data.get('kill_time') if not kill_time: raise BadRequest400('Time not specified') with convert_internal_errors(): seconds = recipe.extend(kill_time) return jsonify({'seconds': seconds})
def update_group(group_name): """ Updates attributes of an existing group. The request body must be a JSON object containing one or more of the following keys. :jsonparam string group_name: New name for the group. :jsonparam string display_name: Display name of the group. :jsonparam string description: Description of the group. :jsonparam string root_password: Optional password. Can be an empty string. If empty, group jobs will use the root password preferences of the job submitter. :jsonparam string membership_type: New membership type for the group. See `POST /groups/` for more information. :status 200: Group was updated. :status 400: Invalid data was given. """ group = _get_group_by_name(group_name) if not group.can_edit(identity.current.user): raise Forbidden403('Cannot edit group') data = read_json_request(request) with convert_internal_errors(): user = identity.current.user renamed = False if 'group_name' in data: new_name = data['group_name'] if new_name != group.group_name: if Group.query.filter(Group.group_name == new_name).count(): raise Conflict409('Group %s already exists' % new_name) group.set_name(user, u'HTTP', new_name) renamed = True if 'display_name' in data: new_display_name = data['display_name'] if new_display_name != group.display_name: group.set_display_name(user, u'HTTP', new_display_name) if 'description' in data: new_description = data['description'] if new_description != group.description: group.set_description(user, u'HTTP', new_description) if 'root_password' in data: new_root_password = data['root_password'] if new_root_password != group.root_password: group.set_root_password(user, u'HTTP', new_root_password) # for backwards compatibility if data.pop('ldap', False): data['membership_type'] = 'ldap' if 'membership_type' in data: new_type = GroupMembershipType.from_string( data['membership_type']) if (new_type == GroupMembershipType.ldap and not group.can_edit_ldap(user)): raise BadRequest400('Cannot edit LDAP group %s' % group) if new_type != group.membership_type: group.membership_type = new_type response = jsonify(group.to_json()) if renamed: response.headers.add('Location', absolute_url(group.href)) return response
def update_reservation_request(id): """ Updates the reservation request of a recipe. The request must be :mimetype:`application/json`. :param id: Recipe's id. :jsonparam boolean reserve: Whether the system will be reserved at the end of the recipe. If true, the system will be reserved. If false, the system will not be reserved. :jsonparam int duration: Number of seconds to rerserve the system. """ recipe = _get_recipe_by_id(id) if not recipe.can_update_reservation_request(identity.current.user): raise Forbidden403('Cannot update the reservation request of recipe %s' % recipe.id) data = read_json_request(request) if 'reserve' not in data: raise BadRequest400('No reserve specified') with convert_internal_errors(): if data['reserve']: if 'duration' not in data: raise BadRequest400('No duration specified') duration = data['duration'] if duration > MAX_SECONDS_PROVISION: raise BadRequest400('Reservation time exceeds maximum time of %s hours' % MAX_HOURS_PROVISION) if recipe.reservation_request: old_duration = recipe.reservation_request.duration recipe.reservation_request.duration = data['duration'] _record_activity(recipe, u'Reservation Request', old_duration, data['duration']) else: reservation_request = RecipeReservationRequest(data['duration']) recipe.reservation_request = reservation_request _record_activity(recipe, u'Reservation Request', None, reservation_request.duration, 'Changed') return jsonify(recipe.reservation_request.__json__()) else: if recipe.reservation_request: session.delete(recipe.reservation_request) _record_activity(recipe, u'Reservation Request', recipe.reservation_request.duration, None) return jsonify(RecipeReservationRequest.empty_json())
def add_access_policy_rule(pool_name): """ Adds a new rule to the access policy for a system pool. Each rule in the policy grants a permission to a single user, a group of users, or to everybody. See :ref:`system-access-policies-api` for a description of the expected JSON parameters. :param pool_name: System pool's name. """ pool = _get_pool_by_name(pool_name) if not pool.can_edit_policy(identity.current.user): raise Forbidden403('Cannot edit system pool policy') policy = pool.access_policy rule = read_json_request(request) if rule.get('user', None): user = User.by_user_name(rule['user']) if not user: raise BadRequest400("User '%s' does not exist" % rule['user']) else: user = None if rule.get('group', None): try: group = Group.by_name(rule['group']) except NoResultFound: raise BadRequest400("Group '%s' does not exist" % rule['group']) else: group = None try: permission = SystemPermission.from_string(rule['permission']) except ValueError: raise BadRequest400('Invalid permission') new_rule = policy.add_rule(user=user, group=group, everybody=rule['everybody'], permission=permission) pool.record_activity(user=identity.current.user, service=u'HTTP', field=u'Access Policy Rule', action=u'Added', new=repr(new_rule)) return '', 204
def _get_owner(data): if data is None: data = {} user_name = data.get('user_name') group_name = data.get('group_name') if user_name and group_name: raise Forbidden403('System pool can have either an user or a group as owner') if user_name: owner = User.by_user_name(user_name) if owner is None: raise BadRequest400('No such user %s' % user_name) owner_type = 'user' if group_name: try: owner = Group.by_name(group_name) except NoResultFound: raise BadRequest400('No such group %r' % group_name) owner_type = 'group' return owner, owner_type
def add_system_to_pool(pool_name): """ Add a system to a system pool :param pool_name: System pool's name. :jsonparam fqdn: System's fully-qualified domain name. """ u = identity.current.user data = read_json_request(request) pool = _get_pool_by_name(pool_name, lockmode='update') if 'fqdn' not in data: raise BadRequest400('System FQDN not specified') try: system = System.by_fqdn(data['fqdn'], u) except NoResultFound: raise BadRequest400("System '%s' does not exist" % data['fqdn']) if not pool in system.pools: if pool.can_edit(u) and system.can_edit(u): system.record_activity(user=u, service=u'HTTP', action=u'Added', field=u'Pool', old=None, new=unicode(pool)) system.pools.append(pool) system.date_modified = datetime.datetime.utcnow() pool.record_activity(user=u, service=u'HTTP', action=u'Added', field=u'System', old=None, new=unicode(system)) else: if not pool.can_edit(u): raise Forbidden403('You do not have permission to ' 'add systems to pool %s' % pool.name) if not system.can_edit(u): raise Forbidden403('You do not have permission to ' 'modify system %s' % system.fqdn) return '', 204
def delete_keystone_trust(username): """ Deletes the Keystone trust for a user account. :param username: The user's username. """ user = _get_user(username) if not config.get('openstack.identity_api_url'): raise BadRequest400("OpenStack Integration is not enabled") if not user.can_edit_keystone_trust(identity.current.user): raise Forbidden403('Cannot edit Keystone trust of user %s' % username) if not user.openstack_trust_id: raise BadRequest400('No Keystone trust created by %s' % user) manager = dynamic_virt.VirtManager(user) manager.delete_keystone_trust() old_trust_id = user.openstack_trust_id user.openstack_trust_id = None user.record_activity(user=identity.current.user, service=u'HTTP', field=u'OpenStack Trust ID', action=u'Deleted', old=old_trust_id) return '', 204
def _create_keystone_trust(user): if not config.get('openstack.identity_api_url'): raise BadRequest400("OpenStack Integration is not enabled") if not user.can_edit_keystone_trust(identity.current.user): raise Forbidden403('Cannot edit Keystone trust of user %s' % user.username) data = read_json_request(request) if 'openstack_username' not in data: raise BadRequest400('No OpenStack username specified') if 'openstack_password' not in data: raise BadRequest400('No OpenStack password specified') if 'openstack_project_name' not in data: raise BadRequest400('No OpenStack project name specified') try: trust_id = dynamic_virt.create_keystone_trust(data['openstack_username'], data['openstack_password'], data['openstack_project_name']) except ValueError as err: raise BadRequest400(u'Could not authenticate with OpenStack using your credentials: %s' % unicode(err)) user.openstack_trust_id = trust_id user.record_activity(user=identity.current.user, service=u'HTTP', field=u'OpenStack Trust ID', action=u'Changed') return jsonify({'openstack_trust_id': trust_id})
def system_command(fqdn): system = _get_system_by_FQDN(fqdn) if not system.lab_controller: raise BadRequest400('System is not attached to a lab controller') if not system.can_power(identity.current.user): raise Forbidden403('You do not have permission to control this system') # We accept JSON or form-encoded for convenience if request.json: if 'action' not in request.json: raise BadRequest400('Missing action key') action = request.json['action'] elif request.form: if 'action' not in request.form: raise BadRequest400('Missing action parameter') action = request.form['action'] else: raise UnsupportedMediaType415 if action == 'reboot': raise BadRequest400('"reboot" is not a valid power command, ' 'send "off" followed by "on" instead') elif action in ['on', 'off', 'interrupt']: if not system.power: raise BadRequest400('System is not configured for power support') command = system.action_power(service=u'HTTP', action=action) elif action == 'clear_netboot': command = system.clear_netboot(service=u'HTTP') else: raise BadRequest400('Unknown action %r' % action) session.flush() # for created attribute return jsonify(command.__json__())
def add_system_access_policy_rule(fqdn): system = _get_system_by_FQDN(fqdn) if not system.can_edit_policy(identity.current.user): raise Forbidden403('Cannot edit system policy') if system.custom_access_policy: policy = system.custom_access_policy else: policy = system.custom_access_policy = SystemAccessPolicy() rule = read_json_request(request) if rule['user']: user = User.by_user_name(rule['user']) if not user: raise BadRequest400("User '%s' does not exist" % rule['user']) else: user = None if rule['group']: try: group = Group.by_name(rule['group']) except NoResultFound: raise BadRequest400("Group '%s' does not exist" % rule['group']) else: group = None try: permission = SystemPermission.from_string(rule['permission']) except ValueError: raise BadRequest400 new_rule = policy.add_rule(user=user, group=group, everybody=rule['everybody'], permission=permission) system.record_activity(user=identity.current.user, service=u'HTTP', field=u'Access Policy Rule', action=u'Added', new=repr(new_rule)) return '', 204
def add_submission_delegate(username): """ Adds a submission delegate for a user account. Submission delegates are other users who are allowed to submit jobs on behalf of this user. :param username: The user's username. :jsonparam string user_name: The submission delegate's username. """ user = _get_user(username) if not user.can_edit(identity.current.user): raise Forbidden403('Cannot edit user %s' % user) data = read_json_request(request) if 'user_name' not in data: raise BadRequest400('Missing "user_name" key to specify submission delegate') submission_delegate = User.by_user_name(data['user_name']) if submission_delegate is None: raise BadRequest400('Submission delegate %s does not exist' % data['user_name']) try: user.add_submission_delegate(submission_delegate, service=u'HTTP') except NoChangeException as e: raise Conflict409(unicode(e)) return 'Added', 201
def update_user(user, display_name=None, email_address=None, password=''): if user.lab_controller: raise BadRequest400( 'User %s is already associated with lab controller %s' % (user, user.lab_controller)) user.display_name = display_name user.email_address = email_address if password: user.password = password group = Group.by_name(u'lab_controller') if group not in user.groups: group.add_member(user, agent=identity.current.user) return user
def get_system_access_policy(fqdn): # XXX need to consolidate this with SystemAccessPolicy.__json__ # (maybe get rid of filtering here and implement it client side instead) system = _get_system_by_FQDN(fqdn) policy = system.custom_access_policy # For now, we don't distinguish between an empty policy and an absent one. if not policy: return jsonify(SystemAccessPolicy.empty_json()) # filtering, if any if len(request.args.keys()) > 1: raise BadRequest400('Only one filtering criteria allowd') query = SystemAccessPolicyRule.query.\ filter(SystemAccessPolicyRule.policy == policy) if request.args.get('mine'): if not identity.current.user: raise Unauthorised401( "The 'mine' access policy filter requires authentication") query = query.join(SystemAccessPolicyRule.user)\ .filter(User.user_name.in_([identity.current.user.user_name])) elif request.args.get('user', None): query = query.join(SystemAccessPolicyRule.user)\ .filter(User.user_name.in_(request.args.getlist('user'))) elif request.args.get('group', None): query = query.join(SystemAccessPolicyRule.group)\ .filter(Group.group_name.in_(request.args.getlist('group'))) return jsonify({ 'id': policy.id, 'rules': [{ 'id': rule.id, 'user': rule.user.user_name if rule.user else None, 'group': rule.group.group_name if rule.group else None, 'everybody': rule.everybody, 'permission': unicode(rule.permission) } for rule in query], 'possible_permissions': [{ 'value': unicode(permission), 'label': unicode(permission.label) } for permission in SystemPermission], })
def post_recipeset_comment(id): """ Adds a new comment to a recipe set. The request must be :mimetype:`application/json`. :param id: ID of the recipe set. :jsonparam string comment: Comment text. """ recipeset = _get_rs_by_id(id) if not recipeset.can_comment(identity.current.user): raise Forbidden403('Cannot post recipe set comment') data = read_json_request(request) if 'comment' not in data: raise BadRequest400('Missing "comment" key') with convert_internal_errors(): comment = RecipeSetComment(user=identity.current.user, comment=data['comment']) recipeset.comments.append(comment) session.flush() # to populate the id return jsonify(comment.__json__())
def add_permission(group_name): """ Add a permission to a group. :param group_name: Group's name. :jsonparam permission_name: Permission's name. """ u = identity.current.user data = read_json_request(request) group = _get_group_by_name(group_name, lockmode='update') if 'permission_name' not in data: raise BadRequest400('Permission name not specified') permission = _get_permission_by_name(data['permission_name']) if permission not in group.permissions: group.permissions.append(permission) group.record_activity(user=u, service=u'HTTP', action=u'Added', field=u'Permission', old=None, new=unicode(permission)) return '', 204
def extend_watchdog_by_taskspec(taskspec): """ Extend the watchdog for a recipe identified by a taskspec. The valid type of a taskspec is either R(recipe) or T(recipe-task). See :ref:`Specifying tasks <taskspec>` in :manpage:`bkr(1)`. :param taskspec: A taskspec argument that identifies a recipe or recipe task. :jsonparam string kill_time: Time in seconds to extend the watchdog by. """ if not taskspec.startswith(('R', 'T')): raise BadRequest400('Taskspec type must be one of [R, T]') try: obj = TaskBase.get_by_t_id(taskspec) except BeakerException as exc: raise NotFound404(unicode(exc)) if isinstance(obj, Recipe): recipe = obj else: recipe = obj.recipe data = read_json_request(request) return _extend_watchdog(recipe.id, data)
def grant_ownership(group_name): """ Grant group ownership to a user. The user can either be the group member or not. If the user is not the group member, it will be added first. :param group_name: Group's name. :jsonparam string user_name: User's username. """ u = identity.current.user data = read_json_request(request) group = _get_group_by_name(group_name, lockmode='update') if not group.can_modify_ownership(identity.current.user): raise Forbidden403('Cannot edit ownership of group %s' % group_name) if 'user_name' not in data: raise BadRequest400('User not specified') user_name = data['user_name'] user = _get_user_by_username(user_name) if not group.has_owner(user): group.grant_ownership(user, agent=identity.current.user) else: raise Conflict409('User %s is already an owner of group %s' % (user.user_name, group_name)) return '', 204
def _get_permission_by_name(permission_name): try: return Permission.by_name(permission_name) except NoResultFound: # Needs to return 400 as the resource exists but the given parameter is bad. raise BadRequest400("Permission '%s' does not exist" % permission_name)
def create_group(): """ Creates a new user group in Beaker. The request must be :mimetype:`application/json`. :jsonparam string group_name: Symbolic name for the group. :jsonparam string display_name: Human-friendly display name for the group. :jsonparam string description: Description of the group. :jsonparam string root_password: Optional root password for group jobs. If this is not set, group jobs will use the root password preferences of the job submitter. :jsonparam string membership_type: Specifies how group membership is populated. Possible values are: * normal: Group is initially empty, members are explicitly added and removed by group owner. * ldap: Membership is populated from the LDAP group with the same group name. * inverted: Group contains all Beaker users *except* users who have been explicitly excluded by the group owner. :status 201: The group was successfully created. """ user = identity.current.user data = read_json_request(request) if 'group_name' not in data: raise BadRequest400('Missing group_name key') if 'display_name' not in data: raise BadRequest400('Missing display_name key') # for backwards compatibility if data.pop('ldap', False): data['membership_type'] = 'ldap' if data.get('membership_type') == 'ldap': if not config.get("identity.ldap.enabled", False): raise BadRequest400('LDAP is not enabled') if not identity.current.user.is_admin(): raise BadRequest400('Only admins can create LDAP groups') try: Group.by_name(data['group_name']) except NoResultFound: pass else: raise Conflict409("Group already exists: %s" % data['group_name']) with convert_internal_errors(): group = Group.lazy_create(group_name=data['group_name']) group.display_name = data['display_name'] group.description = data.get('description') group.root_password = data.get('root_password') session.add(group) group.record_activity(user=user, service=u'HTTP', field=u'Group', action=u'Created') if data.get('membership_type'): group.membership_type = GroupMembershipType.from_string( data['membership_type']) if group.membership_type == GroupMembershipType.ldap: group.refresh_ldap_members() else: # LDAP groups don't have any owners group.add_member(user, is_owner=True, agent=identity.current.user) response = jsonify(group.__json__()) response.status_code = 201 response.headers.add('Location', absolute_url(group.href)) return response
def update_labcontroller(fqdn): """ Updates attributes of the lab controller identified by it's FQDN. The request body must be a json object or only the FQDN if that is the only value to be updated. :param string fqdn: Lab controller's new fully-qualified domain name. :jsonparam string user_name: User name associated with the lab controller. :jsonparam string email_address: Email of the user account associated with the lab controller. :jsonparam string password: Optional password for the user account used to login. :jsonparam string removed: If True, detaches all systems, cancels all running recipes and removes associated distro trees. If False, restores the lab controller. :jsonparam bool disabled: Whether the lab controller should be disabled. New recipes are not scheduled on a lab controller while it is disabled. :status 200: LabController updated. :status 400: Invalid data was given. """ labcontroller = find_labcontroller_or_raise404(fqdn) if not labcontroller.can_edit(identity.current.user): raise Forbidden403('Cannot edit lab controller') data = read_json_request(request) with convert_internal_errors(): # should the lab controller be removed? if data.get('removed', False) and not labcontroller.removed: remove_labcontroller(labcontroller) # should the controller be restored? if data.get('removed') is False and labcontroller.removed: restore_labcontroller(labcontroller) fqdn_changed = False new_fqdn = data.get('fqdn', fqdn) if labcontroller.fqdn != new_fqdn: lc = None try: lc = LabController.by_name(new_fqdn) except NoResultFound: pass if lc is not None: raise BadRequest400('FQDN %s already in use' % new_fqdn) labcontroller.record_activity(user=identity.current.user, service=u'HTTP', field=u'fqdn', action=u'Changed', old=labcontroller.fqdn, new=new_fqdn) labcontroller.fqdn = new_fqdn labcontroller.user.display_name = new_fqdn fqdn_changed = True if 'user_name' in data: user = find_user_or_create(data['user_name']) if labcontroller.user != user: user = update_user(user, display_name=new_fqdn, email_address=data.get( 'email_address', user.email_address), password=data.get('password', user.password)) labcontroller.record_activity(user=identity.current.user, service=u'HTTP', field=u'User', action=u'Changed', old=labcontroller.user.user_name, new=user.user_name) labcontroller.user = user if 'email_address' in data: new_email_address = data.get('email_address') if labcontroller.user.email_address != new_email_address: labcontroller.user.email_address = new_email_address if data.get('password') is not None: labcontroller.user.password = data.get('password') if labcontroller.disabled != data.get('disabled', labcontroller.disabled): labcontroller.record_activity(user=identity.current.user, service=u'HTTP', field=u'disabled', action=u'Changed', old=unicode(labcontroller.disabled), new=data['disabled']) labcontroller.disabled = data['disabled'] response = jsonify(labcontroller.__json__()) if fqdn_changed: response.headers.add('Location', absolute_url(labcontroller.href)) return response
def update_system(fqdn): system = _get_system_by_FQDN(fqdn) if not system.can_edit(identity.current.user): raise Forbidden403('Cannot edit system') data = read_json_request(request) # helper for recording activity below def record_activity(field, old, new, action=u'Changed'): system.record_activity(user=identity.current.user, service=u'HTTP', action=action, field=field, old=old, new=new) with convert_internal_errors(): # XXX what a nightmare... need to use a validation/conversion library, # and maybe simplify/relocate the activity recording stuff somehow changed = False renamed = False if 'fqdn' in data: new_fqdn = data['fqdn'].lower() if new_fqdn != system.fqdn: if System.query.filter(System.fqdn == new_fqdn).count(): raise Conflict409('System %s already exists' % new_fqdn) record_activity(u'FQDN', system.fqdn, new_fqdn) system.fqdn = new_fqdn changed = True renamed = True if 'owner' in data and data['owner'].get( 'user_name') != system.owner.user_name: if not system.can_change_owner(identity.current.user): raise Forbidden403('Cannot change owner') new_owner = User.by_user_name(data['owner'].get('user_name')) if new_owner is None: raise BadRequest400('No such user %s' % data['owner'].get('user_name')) record_activity(u'Owner', system.owner, new_owner) system.owner = new_owner changed = True if 'status' in data: new_status = SystemStatus.from_string(data['status']) if new_status != system.status: record_activity(u'Status', system.status, new_status) system.status = new_status if not new_status.bad and system.status_reason: # clear the status reason for "good" statuses record_activity(u'Status Reason', system.status_reason, None) system.status_reason = None changed = True if 'status_reason' in data: new_reason = data['status_reason'] or None if new_reason and not system.status.bad: raise ValueError('Cannot set status reason when status is %s' % system.status) if new_reason != system.status_reason: record_activity(u'Status Reason', system.status_reason, new_reason) system.status_reason = new_reason changed = True if 'type' in data: new_type = SystemType.from_string(data['type']) if new_type != system.type: record_activity(u'Type', system.type, new_type) system.type = new_type changed = True if 'arches' in data: new_arches = [Arch.by_name(a) for a in (data['arches'] or [])] added_arches = set(new_arches).difference(system.arch) removed_arches = set(system.arch).difference(new_arches) if added_arches or removed_arches: for added_arch in added_arches: record_activity(u'Arch', None, added_arch, u'Added') for removed_arch in removed_arches: record_activity(u'Arch', removed_arch, None, u'Removed') system.arch[:] = new_arches changed = True if 'lab_controller_id' in data: if data['lab_controller_id']: new_lc = LabController.by_id(data['lab_controller_id']) else: new_lc = None if new_lc != system.lab_controller: if system.open_reservation is not None: raise Conflict409( 'Unable to change lab controller while system ' 'is in use (return the system first)') record_activity(u'Lab Controller', system.lab_controller, new_lc) system.lab_controller = new_lc changed = True # If we're given any power-related keys, need to ensure system.power exists if not system.power and set(['power_type', 'power_address', 'power_user', 'power_password', 'power_id', 'power_quiescent_period'])\ .intersection(data.keys()): system.power = Power() if 'power_type' in data: new_power_type = PowerType.by_name(data['power_type']) if new_power_type != system.power.power_type: if not system.power.power_type: old_power_type = '' else: old_power_type = system.power.power_type.name record_activity(u'power_type', old_power_type, new_power_type.name) system.power.power_type = new_power_type changed = True if 'power_address' in data: new_power_address = data['power_address'] if not new_power_address: raise ValueError('Power address is required') if new_power_address != system.power.power_address: record_activity(u'power_address', system.power.power_address, data['power_address']) system.power.power_address = new_power_address changed = True if 'power_user' in data: new_power_user = data['power_user'] or u'' if new_power_user != (system.power.power_user or u''): record_activity(u'power_user', u'********', u'********') system.power.power_user = new_power_user changed = True if 'power_password' in data: new_power_password = data['power_password'] or u'' if new_power_password != (system.power.power_passwd or u''): record_activity(u'power_passwd', u'********', u'********') system.power.power_passwd = new_power_password changed = True if 'power_id' in data: new_power_id = data['power_id'] or u'' if new_power_id != (system.power.power_id or u''): record_activity(u'power_id', system.power.power_id, new_power_id) system.power.power_id = new_power_id changed = True if 'power_quiescent_period' in data: new_qp = int(data['power_quiescent_period']) if new_qp != system.power.power_quiescent_period: record_activity(u'power_quiescent_period', system.power.power_quiescent_period, new_qp) system.power.power_quiescent_period = new_qp changed = True if 'release_action' in data: new_release_action = ReleaseAction.from_string( data['release_action']) if new_release_action != (system.release_action or ReleaseAction.power_off): record_activity( u'release_action', (system.release_action or ReleaseAction.power_off), new_release_action) system.release_action = new_release_action changed = True if 'reprovision_distro_tree' in data: if (not data['reprovision_distro_tree'] or 'id' not in data['reprovision_distro_tree']): new_rpdt = None else: new_rpdt = DistroTree.by_id( data['reprovision_distro_tree']['id']) if new_rpdt != system.reprovision_distro_tree: record_activity(u'reprovision_distro_tree', unicode(system.reprovision_distro_tree), unicode(new_rpdt)) system.reprovision_distro_tree = new_rpdt changed = True if 'location' in data: new_location = data['location'] or None if new_location != system.location: record_activity(u'Location', system.location, new_location) system.location = new_location changed = True if 'lender' in data: new_lender = data['lender'] or None if new_lender != system.lender: record_activity(u'Lender', system.lender, new_lender) system.lender = new_lender changed = True if 'kernel_type' in data: new_kernel_type = KernelType.by_name(data['kernel_type']) if new_kernel_type != system.kernel_type: record_activity(u'Kernel Type', system.kernel_type, new_kernel_type) system.kernel_type = new_kernel_type changed = True if 'hypervisor' in data: if data['hypervisor']: new_hypervisor = Hypervisor.by_name(data['hypervisor']) else: new_hypervisor = None if new_hypervisor != system.hypervisor: record_activity(u'Hypervisor', system.hypervisor, new_hypervisor) system.hypervisor = new_hypervisor changed = True if 'vendor' in data: new_vendor = data['vendor'] or None if new_vendor != system.vendor: record_activity(u'Vendor', system.vendor, new_vendor) system.vendor = new_vendor changed = True if 'model' in data: new_model = data['model'] or None if new_model != system.model: record_activity(u'Model', system.model, new_model) system.model = new_model changed = True if 'serial_number' in data: new_serial_number = data['serial_number'] or None if new_serial_number != system.serial: record_activity(u'Serial Number', system.serial, new_serial_number) system.serial = new_serial_number changed = True if 'mac_address' in data: new_mac_address = data['mac_address'] or None if new_mac_address != system.mac_address: record_activity(u'MAC Address', system.mac_address, new_mac_address) system.mac_address = new_mac_address changed = True if 'memory' in data: new_memory = int(data['memory']) if data['memory'] else None if new_memory != system.memory: record_activity(u'Memory', system.memory, new_memory) system.memory = new_memory changed = True if 'numa_nodes' in data: new_numa_nodes = int( data['numa_nodes']) if data['numa_nodes'] else None if not system.numa: system.numa = Numa() if new_numa_nodes != system.numa.nodes: record_activity(u'NUMA/Nodes', system.numa.nodes, new_numa_nodes) system.numa.nodes = new_numa_nodes changed = True if changed: # XXX clear checksum!? system.date_modified = datetime.datetime.utcnow() response = jsonify(system.__json__()) if renamed: response.headers.add('Location', url('/view/%s' % system.fqdn)) return response