def system_problem_report(system, description, recipe=None, reporter=None): if reporter is not None: sender = _sender_details(reporter) else: sender = config.get('beaker_email') if not sender: log.warning("beaker_email not defined in app.cfg; unable to send mail") return body = [_(u'A Beaker user has reported a problem with system \n%s <%s>.') % (system.fqdn, absolute_url('/view/%s' % system.fqdn)), ''] if reporter is not None: body.append(_(u'Reported by: %s') % reporter.display_name) if recipe is not None: body.append(_(u'Related to: %s <%s>') % (recipe.t_id, absolute_url('/recipes/%s' % recipe.id))) body.extend(['', unicode(_(u'Problem description:')), description]) headers=[('X-Beaker-Notification', 'system-problem'), ('X-Beaker-System', system.fqdn), ('X-Lender', system.lender or ''), ('X-Owner', system.owner), ('X-Location', system.location or ''), ('X-Lab-Controller', system.lab_controller or ''), ('X-Vendor', system.vendor or ''), ('X-Type', system.type)] arch_headers = [('X-Arch', arch) for arch in system.arch] headers.extend(arch_headers) cc = [] if reporter is not None: cc.append(reporter.email_address) cc.extend(system.cc) send_mail(sender, system.owner.email_address, _(u'Problem reported for %s') % system.fqdn, '\n'.join(body), cc=cc, headers=headers)
def test_delayed_jobs(self): with session.begin(): # Create a queued job that was submitted a long time ago recipe = data_setup.create_recipe() job = data_setup.create_job_for_recipes([recipe]) job.owner = self.user data_setup.mark_job_queued(job) job.recipesets[0].queue_time = datetime.utcnow() - timedelta(days=self.delayed_job_age) # create a job with two recipes, one Queued and one Scheduled # which was submitted a long time ago queued_recipe = data_setup.create_recipe() scheduled_recipe = data_setup.create_recipe() job_with_multiple_recipes = data_setup.create_job_for_recipes([queued_recipe, scheduled_recipe]) job_with_multiple_recipes.owner = self.user # mark recipe Queued queued_recipe.process() queued_recipe.queue() # mark recipe Scheduled scheduled_recipe.process() scheduled_recipe.queue() scheduled_recipe.schedule() data_setup.mark_job_queued(job_with_multiple_recipes) job_with_multiple_recipes.recipesets[0].queue_time = datetime.utcnow()\ - timedelta(days=self.delayed_job_age) # create a new submmited job for just now recently_submmited_job = data_setup.create_job_for_recipes([recipe]) recently_submmited_job.owner = self.user data_setup.mark_job_queued(recently_submmited_job) beaker_usage = BeakerUsage(self.user, self.reservation_expiry, self.reservation_length, self.waiting_recipe_age, self.delayed_job_age) delayed_jobs = beaker_usage.delayed_jobs() self.assertEqual(len(delayed_jobs), 2) self.assertEqual(absolute_url('/jobs/%s' % job.id), delayed_jobs[0][1]) self.assertEqual(absolute_url('/jobs/%s' % job_with_multiple_recipes.id), delayed_jobs[1][1])
def broken_system_notify(system, reason, recipe=None): if system.owner.notify_broken_system: sender = config.get('beaker_email') if not sender: log.warning("beaker_email not defined in app.cfg; unable to send mail") return body = [_(u'Beaker has automatically marked system \n%s <%s> \nas broken, due to:') % (system.fqdn, absolute_url('/view/%s' % system.fqdn)), '', reason, '', unicode(_(u'Please investigate this error and take appropriate action.')), ''] if recipe: body.extend([_(u'Failure occurred in %s <%s>') % (recipe.t_id, absolute_url('/recipes/%s' % recipe.id)), '']) if system.power: body.extend([_(u'Power type: %s') % system.power.power_type.name, _(u'Power address: %s') % system.power.power_address, _(u'Power id: %s') % system.power.power_id]) send_mail(sender, system.owner.email_address, _(u'System %s automatically marked broken') % system.fqdn, '\n'.join(body), cc=system.cc, headers=[('X-Beaker-Notification', 'system-broken'), ('X-Beaker-System', system.fqdn), ('X-Lender', system.lender or ''), ('X-Owner', system.owner), ('X-Location', system.location or ''), ('X-Lab-Controller', system.lab_controller or ''), ('X-Vendor', system.vendor or ''), ('X-Type', system.type)] + [('X-Arch', arch) for arch in system.arch])
def broken_system_notify(system, reason, recipe=None): sender = config.get('beaker_email') if not sender: log.warning("beaker_email not defined in app.cfg; unable to send mail") return body = [_(u'Beaker has automatically marked system \n%s <%s> \nas broken, due to:') % (system.fqdn, absolute_url('/view/%s' % system.fqdn)), '', reason, '', unicode(_(u'Please investigate this error and take appropriate action.')), ''] if recipe: body.extend([_(u'Failure occurred in %s <%s>') % (recipe.t_id, absolute_url('/recipes/%s' % recipe.id)), '']) if system.power: body.extend([_(u'Power type: %s') % system.power.power_type.name, _(u'Power address: %s') % system.power.power_address, _(u'Power id: %s') % system.power.power_id]) send_mail(sender, system.owner.email_address, _(u'System %s automatically marked broken') % system.fqdn, '\n'.join(body), cc=system.cc, headers=[('X-Beaker-Notification', 'system-broken'), ('X-Beaker-System', system.fqdn), ('X-Lender', system.lender or ''), ('X-Owner', system.owner), ('X-Location', system.location or ''), ('X-Lab-Controller', system.lab_controller or ''), ('X-Vendor', system.vendor or ''), ('X-Type', system.type)] + [('X-Arch', arch) for arch in system.arch])
def __init__(self, message=None): self._message = message or _(u'Please log in') self.args = [self._message] if cherrypy.request.method not in ('GET', 'HEAD'): # Other HTTP methods cannot be safely redirected through the login # form, so we will just show a 403. self.status = 403 else: self.status = 302 if current.anonymous: forward_url = cherrypy.request.path if cherrypy.request.query_string: forward_url += '?%s' % cherrypy.request.query_string self.urls = [absolute_url('/login', forward_url=forward_url)] else: self.urls = [absolute_url('/forbidden', reason=message)]
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' if job_details['pick'] == 'fqdn': try: job_details['system'] = System.by_fqdn(request.form.get('system'), identity.current.user) except NoResultFound: 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')) except NoResultFound: raise BadRequest400('Lab controller %s not found' % request.form.get('lab')) days = int(request.form.get('reserve_days') or DEFAULT_RESERVE_DAYS) days = min(days, MAX_DAYS_PROVISION) job_details['reservetime'] = days * 24 * 60 * 60 job_details['whiteboard'] = request.form.get('whiteboard') 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 describe_device_class(device_class, graph): concept = URIRef( absolute_url('/devices/%s' % urllib.quote(device_class.device_class.encode('utf8'), '')) + '#class') graph.add((concept, RDF.type, INV.DeviceClass)) graph.add((concept, RDFS.label, Literal(device_class.device_class))) return concept
def describe_arch(arch, graph): concept = URIRef( absolute_url('/arch/%s' % urllib.quote(arch.arch.encode('utf8'), '')) + '#arch') graph.add((concept, RDF.type, INV.Arch)) graph.add((concept, RDFS.label, Literal(arch.arch))) return concept
def describe_key(key, graph): concept = URIRef( absolute_url('/keys/%s' % urllib.quote(key.key_name.encode('utf8'), '')) + '#key') graph.add((concept, RDF.type, RDF.Property)) graph.add((concept, RDFS.label, Literal(key.key_name))) return concept
def describe_bus_type(bus_type, graph): concept = URIRef( absolute_url('/devices/bus/%s' % urllib.quote(bus_type.encode('utf8'), '')) + '#bus') graph.add((concept, RDF.type, INV.BusType)) graph.add((concept, RDFS.label, Literal(bus_type))) return concept
def system_loan_request(system, message, requester, requestee_email): sender = _sender_details(requester) body = [ _( u"A Beaker user has requested you loan them the system\n%s <%s>.\n" "Here is a copy of their request:\n" "%s\n Requested by: %s" ) % (system.fqdn, absolute_url("/view/%s" % system.fqdn), message, requester.display_name), "", ] headers = [ ("X-Beaker-Notification", "loan-request"), ("X-Beaker-System", system.fqdn), ("X-Lender", system.lender or ""), ("X-Owner", system.owner), ("X-Location", system.location or ""), ("X-Lab-Controller", system.lab_controller or ""), ("X-Vendor", system.vendor or ""), ("X-Type", system.type), ] arch_headers = [("X-Arch", arch) for arch in system.arch] headers.extend(arch_headers) cc = [requester.email_address] + system.cc send_mail(sender, requestee_email, _(u"Loan request for %s") % system.fqdn, "\n".join(body), cc=cc, headers=headers)
def create_pool(): """ Creates a new system pool in Beaker. The request must be :mimetype:`application/x-www-form-urlencoded` or :mimetype:`application/json`. :jsonparam string name: Name for the system pool. :jsonparam string description: Description of the system pool. :jsonparam object owner: JSON object containing a ``user_name`` key or ``group_name`` key identifying the owner for the system pool. :status 201: The system pool was successfully created. """ owner = None description = None u = identity.current.user if request.json: if 'name' not in request.json: raise BadRequest400('Missing pool name key') new_name = request.json['name'] if 'owner' in request.json: owner = request.json['owner'] if 'description' in request.json: description = request.json['description'] elif request.form: if 'name' not in request.form: raise BadRequest400('Missing pool name parameter') new_name = request.form['name'] if 'owner' in request.form: owner = request.form['owner'] if 'description' in request.form: description = request.form['description'] else: raise UnsupportedMediaType415 with convert_internal_errors(): if SystemPool.query.filter(SystemPool.name == new_name).count() != 0: raise Conflict409('System pool with name %r already exists' % new_name) pool = SystemPool(name=new_name, description=description) session.add(pool) if owner: owner, owner_type = _get_owner(owner) if owner_type == 'user': pool.owning_user = owner else: pool.owning_group = owner else: pool.owning_user = u # new systems pool are visible to everybody by default pool.access_policy = SystemAccessPolicy() pool.access_policy.add_rule(SystemPermission.view, everybody=True) pool.record_activity(user=u, service=u'HTTP', action=u'Created', field=u'Pool', new=unicode(pool)) response = jsonify(pool.__json__()) response.status_code = 201 response.headers.add('Location', absolute_url(pool.href)) return response
def link(self): if self.url: return self.url assert self.id is not None, 'not flushed?' url = absolute_url('/kickstart/%s' % self.id, scheme='http', labdomain=True) return url
def system_loan_request(system, message, requester, requestee_email): sender = _sender_details(requester) body = [ _(u'A Beaker user has requested you loan them the system\n%s <%s>.\n' 'Here is a copy of their request:\n' '%s\n Requested by: %s') % (system.fqdn, absolute_url( '/view/%s' % system.fqdn), message, requester.display_name), '' ] headers = [('X-Beaker-Notification', 'loan-request'), ('X-Beaker-System', system.fqdn), ('X-Lender', system.lender or ''), ('X-Owner', system.owner), ('X-Location', system.location or ''), ('X-Lab-Controller', system.lab_controller or ''), ('X-Vendor', system.vendor or ''), ('X-Type', system.type)] arch_headers = [('X-Arch', arch) for arch in system.arch] headers.extend(arch_headers) cc = [requester.email_address] + system.cc send_mail(sender, requestee_email, _(u'Loan request for %s') % system.fqdn, '\n'.join(body), cc=cc, headers=headers)
def describe_user(user, graph): concept = URIRef( absolute_url('/users/%s' % urllib.quote(user.user_name.encode('utf8'), '')) + '#user') graph.add((concept, RDF.type, FOAF.User)) graph.add((concept, FOAF.name, Literal(user.display_name))) graph.add((concept, FOAF.mbox, URIRef('mailto:' + user.email_address))) return concept
def groups_mine(): """ Redirect for compatibility. """ return flask_redirect( absolute_url('/groups/', q='member.user_name:%s' % identity.current.user.user_name))
def failed_recipes(job): msg = "JobID: %s Status: %s Result: %s <%s>\n" % \ (job.id, job.status, job.result, absolute_url('/jobs/%s' % job.id)) for recipeset in job.recipesets: if recipeset.is_failed(): msg = "%s\tRecipeSetID: %s\n" % (msg, recipeset.id) for recipe in recipeset.recipes: if recipe.is_failed(): msg = "%s\t\tRecipeID: %s Arch: %s System: %s Distro: %s Status: %s Result: %s <%s>\n" \ % (msg, recipe.id, recipe.distro_tree.arch, recipe.resource, recipe.distro_tree.distro, recipe.status, recipe.result, absolute_url('/recipes/%s' % recipe.id)) for task in recipe.tasks: if task.is_failed(): msg = "%s\t\t\tTaskID: %s TaskName: %s StartTime: %s Duration: %s Status: %s Result: %s\n" \ % (msg, task.id, task.name, task.start_time, task.duration, task.status, task.result) return msg
def _create_delayed_job(self): recipe = data_setup.create_recipe() job = data_setup.create_job_for_recipes([recipe]) job.owner = self.user data_setup.mark_job_queued(job) job.recipesets[0].queue_time = datetime.utcnow() - timedelta(days=self.delayed_job_age) email_content = u""" The following jobs you submitted to %s have been queued for more than %s days. Please cancel them if they are no longer relevant, or perhaps arrange a loan of an appropriate system or systems Start time Delayed Job %s %s """ % (absolute_url('/'), self.delayed_job_age, job.recipesets[0].queue_time.strftime('%Y-%m-%d %H:%M:%S'), absolute_url('/jobs/%s') % job.id) return email_content
def failed_recipes(job): msg = "JobID: %s Status: %s Result: %s <%s>\n" % \ (job.id, job.status, job.result, absolute_url('/jobs/%s' % job.id)) for recipeset in job.recipesets: if recipeset.is_failed(): msg = "%s\tRecipeSetID: %s\n" % ( msg, recipeset.id ) for recipe in recipeset.recipes: if recipe.is_failed(): msg = "%s\t\tRecipeID: %s Arch: %s System: %s Distro: %s Status: %s Result: %s <%s>\n" \ % (msg, recipe.id, recipe.distro_tree.arch, recipe.resource, recipe.distro_tree.distro, recipe.status, recipe.result, absolute_url('/recipes/%s' % recipe.id)) for task in recipe.tasks: if task.is_failed(): msg = "%s\t\t\tTaskID: %s TaskName: %s StartTime: %s Duration: %s Status: %s Result: %s\n" \ % (msg, task.id, task.name, task.start_time, task.duration, task.status, task.result) return msg
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' try: Group.by_name(data['group_name']) except NoResultFound: pass else: raise Conflict409("Group '%s' already exists" % 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_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 wrapper(*args, **kwds): if not identity.current.user: if request.method in ['GET', 'HEAD'] and not request_wants_json(): forward_url = request.path if request.query_string: forward_url += '?' + request.query_string return redirect(absolute_url('/login', forward_url=forward_url)) raise Unauthorised401("Authenticated user required") return f(*args, **kwds)
def update_pool(pool_name): """ Updates attributes of an existing system pool. The request body must be a JSON object containing one or more of the following keys. :param pool_name: System pool's name. :jsonparam string name: New name for the system pool. :jsonparam string description: Description of the system pool. :jsonparam object owner: JSON object containing a ``user_name`` key or ``group_name`` key identifying the new owner for the system pool. :status 200: System pool was updated. :status 400: Invalid data was given. """ pool = _get_pool_by_name(pool_name) if not pool.can_edit(identity.current.user): raise Forbidden403('Cannot edit system pool') data = read_json_request(request) # helper for recording activity below def record_activity(field, old, new, action=u'Changed'): pool.record_activity(user=identity.current.user, service=u'HTTP', action=action, field=field, old=old, new=new) with convert_internal_errors(): renamed = False if 'name' in data: new_name = data['name'] if new_name != pool.name: if SystemPool.query.filter( SystemPool.name == new_name).count(): raise Conflict409('System pool %s already exists' % new_name) record_activity(u'Name', pool.name, new_name) pool.name = new_name renamed = True if 'description' in data: new_description = data['description'] if new_description != pool.description: record_activity(u'Description', pool.description, new_description) pool.description = new_description if 'owner' in data: new_owner, owner_type = _get_owner(data['owner']) if owner_type == 'user': pool.change_owner(user=new_owner) else: pool.change_owner(group=new_owner) response = jsonify(pool.__json__()) if renamed: response.headers.add('Location', absolute_url(pool.href)) return response
def broken_system_notify(system, reason, recipe=None): sender = config.get("beaker_email") if not sender: log.warning("beaker_email not defined in app.cfg; unable to send mail") return body = [ _(u"Beaker has automatically marked system \n%s <%s> \nas broken, due to:") % (system.fqdn, absolute_url("/view/%s" % system.fqdn)), "", reason, "", unicode(_(u"Please investigate this error and take appropriate action.")), "", ] if recipe: body.extend([_(u"Failure occurred in %s <%s>") % (recipe.t_id, absolute_url("/recipes/%s" % recipe.id)), ""]) if system.power: body.extend( [ _(u"Power type: %s") % system.power.power_type.name, _(u"Power address: %s") % system.power.power_address, _(u"Power id: %s") % system.power.power_id, ] ) send_mail( sender, system.owner.email_address, _(u"System %s automatically marked broken") % system.fqdn, "\n".join(body), cc=system.cc, headers=[ ("X-Beaker-Notification", "system-broken"), ("X-Beaker-System", system.fqdn), ("X-Lender", system.lender or ""), ("X-Owner", system.owner), ("X-Location", system.location or ""), ("X-Lab-Controller", system.lab_controller or ""), ("X-Vendor", system.vendor or ""), ("X-Type", system.type), ] + [("X-Arch", arch) for arch in system.arch], )
def system_problem_report(system, description, recipe=None, reporter=None): if reporter is not None: sender = _sender_details(reporter) else: sender = config.get("beaker_email") if not sender: log.warning("beaker_email not defined in app.cfg; unable to send mail") return body = [ _(u"A Beaker user has reported a problem with system \n%s <%s>.") % (system.fqdn, absolute_url("/view/%s" % system.fqdn)), "", ] if reporter is not None: body.append(_(u"Reported by: %s") % reporter.display_name) if recipe is not None: body.append(_(u"Related to: %s <%s>") % (recipe.t_id, absolute_url("/recipes/%s" % recipe.id))) body.extend(["", unicode(_(u"Problem description:")), description]) headers = [ ("X-Beaker-Notification", "system-problem"), ("X-Beaker-System", system.fqdn), ("X-Lender", system.lender or ""), ("X-Owner", system.owner), ("X-Location", system.location or ""), ("X-Lab-Controller", system.lab_controller or ""), ("X-Vendor", system.vendor or ""), ("X-Type", system.type), ] arch_headers = [("X-Arch", arch) for arch in system.arch] headers.extend(arch_headers) cc = [] if reporter is not None: cc.append(reporter.email_address) cc.extend(system.cc) send_mail( sender, system.owner.email_address, _(u"Problem reported for %s") % system.fqdn, "\n".join(body), cc=cc, headers=headers, )
def describe_system(system, graph): """ Appends an RDF description of a system to the given graph. """ concept = URIRef(absolute_url(system.href) + '#system') graph.add((concept, RDF.type, INV.System)) graph.add((concept, INV.fqdn, Literal(system.fqdn))) if system.lab_controller: graph.add((concept, INV.controlledBy, describe_lab_controller(system.lab_controller, graph))) if system.vendor: graph.add((concept, INV.vendor, Literal(system.vendor))) if system.model: graph.add((concept, INV.model, Literal(system.model))) if system.location: graph.add((concept, INV.location, Literal(system.location))) if system.mac_address: graph.add((concept, INV.macAddress, Literal(system.mac_address))) if system.owner: graph.add((concept, INV.owner, describe_user(system.owner, graph))) for arch in system.arch: graph.add((concept, INV.supportsArch, describe_arch(arch, graph))) if system.memory: graph.add((concept, INV.memory, Literal(system.memory))) if system.numa: graph.add((concept, INV.numaNodes, Literal(system.numa.nodes))) if system.cpu: if system.cpu.vendor: graph.add((concept, INV.cpuVendor, Literal(system.cpu.vendor))) if system.cpu.model_name: graph.add((concept, INV.cpuModelName, Literal(system.cpu.model_name))) if system.cpu.family: graph.add((concept, INV.cpuFamilyId, Literal(system.cpu.family))) if system.cpu.model: graph.add((concept, INV.cpuModelId, Literal(system.cpu.model))) if system.cpu.stepping: graph.add((concept, INV.cpuStepping, Literal(system.cpu.stepping))) if system.cpu.speed: graph.add((concept, INV.cpuSpeed, Literal(Decimal(str(system.cpu.speed))))) if system.cpu.processors: graph.add((concept, INV.cpuCount, Literal(system.cpu.processors))) if system.cpu.cores: graph.add((concept, INV.cpuCoreCount, Literal(system.cpu.cores))) if system.cpu.sockets: graph.add((concept, INV.cpuSocketCount, Literal(system.cpu.sockets))) if system.cpu.hyper: graph.add((concept, INV.cpuHyperthreading, Literal(system.cpu.hyper))) for flag in system.cpu.flags: graph.add((concept, INV.cpuFlag, Literal(flag.flag))) for device in system.devices: graph.add((concept, INV.hasDevice, describe_device(device, graph))) for kv in chain(system.key_values_int, system.key_values_string): if kv.key: # ugh graph.add((concept, describe_key(kv.key, graph), Literal(kv.key_value)))
def get_labcontrollers(): """Returns a JSON collection of all labcontrollers defined in Beaker.""" labcontrollers = LabController.query.order_by(LabController.fqdn).all() if request_wants_json(): return jsonify(entries=labcontrollers) can_edit = identity.current.user is not None and identity.current.user.is_admin() return render_tg_template('bkr.server.templates.labcontrollers', { 'title': 'Lab Controllers', 'labcontrollers': labcontrollers, 'labcontrollers_url': absolute_url('/labcontrollers/'), 'can_edit': can_edit, })
def delayed_jobs(self): """ Get Delayed Jobs """ query = Job.query.filter(Job.owner == self.user)\ .join(Job.recipesets)\ .filter(and_(RecipeSet.queue_time <= (datetime.utcnow() - timedelta(days=self.delayed_job_age)), RecipeSet.status == TaskStatus.queued))\ .group_by(Job.id)\ .values(func.min(RecipeSet.queue_time), Job.id) return [(queue_time, absolute_url('/jobs/%s' % job_id)) for queue_time, job_id in query]
def _create_expiring_reservation(self): recipe = data_setup.create_recipe_reservation(self.user, u'/distribution/reservesys', (self.reservation_expiry - 1) * 3600) email_content = u""" Your reservations of the following systems in %s are going to expire within %s hours. If you wish to ensure you retain the contents of these systems, please extend your reservation. Expiry Date FQDN %s %s """ % (absolute_url('/'), self.reservation_expiry, recipe.watchdog.kill_time.strftime('%Y-%m-%d %H:%M:%S'), recipe.resource.fqdn) return email_content
def get_group_by_id_or_name(): """ Created for backwards compatibility. Will redirect to /groups/<group_name>. :queryparam group_id: Group's id. :queryparam group_name: Group's name. """ if 'group_id' in request.args: with convert_internal_errors(): group = Group.by_id(request.args['group_id']) elif 'group_name' in request.args: group = _get_group_by_name(request.args['group_name']) else: raise NotFound404 return flask_redirect(absolute_url(group.href))
def test_send_usage_reminder(self): with session.begin(): email_content = self._create_expiring_reservation() email_content += self._create_open_reservation() email_content += self._create_delayed_job() beaker_usage = BeakerUsage(self.user, self.reservation_expiry, self.reservation_length, self.waiting_recipe_age, self.delayed_job_age) current_date = datetime.utcnow().strftime("%Y-%m-%d") data = { 'user_name': self.user.user_name, 'current_date': current_date, 'beaker_fqdn': absolute_url('/'), 'reservation_expiry': self.reservation_expiry, 'reservation_length': self.reservation_length, 'waiting_recipe_age': self.waiting_recipe_age, 'delayed_job_age': self.delayed_job_age, 'expiring_reservations': beaker_usage.expiring_reservations(), 'open_reservations': beaker_usage.open_in_demand_systems(), 'delayed_jobs': beaker_usage.delayed_jobs() } mail_capture_thread.start_capturing() with session.begin(): bkr.server.mail.send_usage_reminder(self.user, data) captured_mails = mail_capture_thread.stop_capturing() self.assertEqual(len(captured_mails), 1) sender, rcpts, raw_msg = captured_mails[0] self.assertEqual(rcpts, [self.user.email_address]) msg = email.message_from_string(raw_msg) self.assertEqual(msg['To'], self.user.email_address) self.assertTrue( msg['Subject'], '[Beaker] Usage report for %s (%s)' % (self.user.user_name, current_date)) expected_mail_body = u"""========= [Beaker] Usage report for %s (%s) ========= Hi %s, %s =========""" % (self.user.user_name, current_date, self.user.user_name, email_content) actual_mail_body = msg.get_payload(decode=True) self.assertEqual(actual_mail_body, expected_mail_body) self.assertEqual(msg['X-Beaker-Notification'], 'usage-report')
def update_pool(pool_name): """ Updates attributes of an existing system pool. The request body must be a JSON object containing one or more of the following keys. :param pool_name: System pool's name. :jsonparam string name: New name for the system pool. :jsonparam string description: Description of the system pool. :jsonparam object owner: JSON object containing a ``user_name`` key or ``group_name`` key identifying the new owner for the system pool. :status 200: System pool was updated. :status 400: Invalid data was given. """ pool = _get_pool_by_name(pool_name) if not pool.can_edit(identity.current.user): raise Forbidden403('Cannot edit system pool') data = read_json_request(request) # helper for recording activity below def record_activity(field, old, new, action=u'Changed'): pool.record_activity(user=identity.current.user, service=u'HTTP', action=action, field=field, old=old, new=new) with convert_internal_errors(): renamed = False if 'name' in data: new_name = data['name'] if new_name != pool.name: if SystemPool.query.filter(SystemPool.name == new_name).count(): raise Conflict409('System pool %s already exists' % new_name) record_activity(u'Name', pool.name, new_name) pool.name = new_name renamed = True if 'description' in data: new_description = data['description'] if new_description != pool.description: record_activity(u'Description', pool.description, new_description) pool.description = new_description if 'owner' in data: new_owner, owner_type = _get_owner(data['owner']) if owner_type == 'user': pool.change_owner(user=new_owner) else: pool.change_owner(group=new_owner) response = jsonify(pool.__json__()) if renamed: response.headers.add('Location', absolute_url(pool.href)) return response
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 test_send_usage_reminder(self): with session.begin(): email_content = self._create_expiring_reservation() email_content += self._create_open_reservation() email_content += self._create_delayed_job() beaker_usage = BeakerUsage(self.user, self.reservation_expiry, self.reservation_length, self.waiting_recipe_age, self.delayed_job_age) current_date = datetime.utcnow().strftime("%Y-%m-%d") data = { 'user_name': self.user.user_name, 'current_date': current_date, 'beaker_fqdn': absolute_url('/'), 'reservation_expiry': self.reservation_expiry, 'reservation_length': self.reservation_length, 'waiting_recipe_age': self.waiting_recipe_age, 'delayed_job_age': self.delayed_job_age, 'expiring_reservations': beaker_usage.expiring_reservations(), 'open_reservations': beaker_usage.open_in_demand_systems(), 'delayed_jobs': beaker_usage.delayed_jobs() } mail_capture_thread.start_capturing() with session.begin(): bkr.server.mail.send_usage_reminder(self.user, data) captured_mails = mail_capture_thread.stop_capturing() self.assertEqual(len(captured_mails),1) sender, rcpts, raw_msg = captured_mails[0] self.assertEqual(rcpts, [self.user.email_address]) msg = email.message_from_string(raw_msg) self.assertEqual(msg['To'], self.user.email_address) self.assertTrue(msg['Subject'], '[Beaker] Usage report for %s (%s)' % (self.user.user_name, current_date)) expected_mail_body = u"""========= [Beaker] Usage report for %s (%s) ========= Hi %s, %s =========""" % (self.user.user_name, current_date, self.user.user_name, email_content) actual_mail_body = msg.get_payload(decode=True) self.assertEqual(actual_mail_body, expected_mail_body) self.assertEqual(msg['X-Beaker-Notification'], 'usage-report')
def generate_image(delete=True): f = tempfile.NamedTemporaryFile(suffix='.beaker-ipxe-image', delete=delete) log.debug('Generating image in %s', f.name) subprocess.check_call(['mformat', '-i', f.name, '-C', '-t', '4', '-h', '64', '-n', '32', '::']) subprocess.check_call(['syslinux', '--install', f.name]) subprocess.check_call(['mcopy', '-i', f.name, '/usr/share/ipxe/ipxe.lkrn', '::ipxe.lkrn']) mcopy = subprocess.Popen(['mcopy', '-i', f.name, '-', '::syslinux.cfg'], stdin=subprocess.PIPE) mcopy.communicate("""\ DEFAULT ipxe LABEL ipxe KERNEL ipxe.lkrn APPEND dhcp && chain %s """ % absolute_url('/systems/by-uuid/${uuid}/ipxe-script', scheme='http', labdomain=True)) if mcopy.returncode != 0: raise RuntimeError('mcopy syslinux.cfg failed with return code %s' % mcopy.returncode) return f
def generate_image(): f = tempfile.NamedTemporaryFile(suffix=".beaker-ipxe-image") log.debug("Generating image in %s", f.name) subprocess.check_call(["mformat", "-i", f.name, "-C", "-t", "4", "-h", "64", "-n", "32", "::"]) subprocess.check_call(["syslinux", "--install", f.name]) subprocess.check_call(["mcopy", "-i", f.name, "/usr/share/ipxe/ipxe.lkrn", "::ipxe.lkrn"]) mcopy = subprocess.Popen(["mcopy", "-i", f.name, "-", "::syslinux.cfg"], stdin=subprocess.PIPE) mcopy.communicate( """\ DEFAULT ipxe LABEL ipxe KERNEL ipxe.lkrn APPEND dhcp && chain %s """ % absolute_url("/systems/by-uuid/${uuid}/ipxe-script", scheme="http", labdomain=True) ) if mcopy.returncode != 0: raise RuntimeError("mcopy syslinux.cfg failed with return code %s" % mcopy.returncode) return f
def generate_image(): f = tempfile.NamedTemporaryFile(suffix='.beaker-ipxe-image') log.debug('Generating image in %s', f.name) f.truncate(4 * 1024 * 1024) # 4MB subprocess.check_call(['mkdosfs', f.name], stdout=open('/dev/null', 'a')) subprocess.check_call(['syslinux', '--install', f.name]) subprocess.check_call(['mcopy', '-i', f.name, '/usr/share/ipxe/ipxe.lkrn', '::ipxe.lkrn']) mcopy = subprocess.Popen(['mcopy', '-i', f.name, '-', '::syslinux.cfg'], stdin=subprocess.PIPE) mcopy.communicate("""\ DEFAULT ipxe LABEL ipxe KERNEL ipxe.lkrn APPEND dhcp && chain %s """ % absolute_url('/systems/by-uuid/${uuid}/ipxe-script', scheme='http', labdomain=True)) if mcopy.returncode != 0: raise RuntimeError('mcopy syslinux.cfg failed with return code %s' % mcopy.returncode) return f
def create_user(): """ Creates a new user account in Beaker. """ data = read_json_request(request) with convert_internal_errors(): new_user_name = data.get('user_name', '').strip() existing_user = User.by_user_name(new_user_name) if existing_user is not None: raise Conflict409('User %s already exists' % new_user_name) new_display_name = data.get('display_name', '').strip() new_email_address = data.get('email_address', '').strip() user = User(user_name=new_user_name, display_name=new_display_name, email_address=new_email_address) session.add(user) session.flush() # to populate id response = jsonify(user_full_json(user)) response.status_code = 201 response.headers.add('Location', absolute_url(user.href)) return response
def _create_open_reservation(self): system = data_setup.create_system() data_setup.create_manual_reservation(system, start=datetime.utcnow() - timedelta(days=self.reservation_length), user=self.user) recipe = data_setup.create_recipe() recipe.systems[:] = [system] job = data_setup.create_job_for_recipes([recipe]) data_setup.mark_job_queued(job) job.recipesets[0].queue_time = datetime.utcnow() - timedelta(hours=self.waiting_recipe_age) email_content = u""" The following systems have been allocated to you in %s for more than %s days and have other recipes queued for longer than %s hours. Please return them if you are no longer using them. Duration Waiting FQDN %s %s %s """ % (absolute_url('/'), self.reservation_length, self.waiting_recipe_age, "%s days" % (datetime.utcnow() - system.reservations[0].start_time).days, "1 recipe", system.fqdn) return email_content
def main(*args): parser = get_parser() (options, args) = parser.parse_args(*args) load_config_or_exit(options.configfile) log_to_stream(sys.stderr) interface.start(config) reservation_expiry = options.reservation_expiry reservation_length = options.reservation_length waiting_recipe_age = options.waiting_recipe_age delayed_job_age = options.delayed_job_age testing = options.testing if testing: print 'Dry run only, nothing will be sent\n' for user in User.query: beaker_usage = BeakerUsage(user, reservation_expiry, reservation_length, waiting_recipe_age, delayed_job_age) expiring_reservations = beaker_usage.expiring_reservations() open_in_demand_systems = beaker_usage.open_in_demand_systems() delayed_jobs = beaker_usage.delayed_jobs() if (expiring_reservations or open_in_demand_systems or delayed_jobs): data = { 'user_name': user.user_name, 'current_date': datetime.utcnow().strftime("%Y-%m-%d"), 'beaker_fqdn': absolute_url('/'), 'reservation_expiry': reservation_expiry, 'reservation_length': reservation_length, 'waiting_recipe_age': waiting_recipe_age, 'delayed_job_age': delayed_job_age, 'expiring_reservations': expiring_reservations, 'open_reservations': open_in_demand_systems, 'delayed_jobs': delayed_jobs } mail.send_usage_reminder(user, data, testing) return