...
 
Commits (2)
......@@ -37,7 +37,8 @@ BIN_SCRIPTS = manage_profile manage_instance manage_dataset \
create_slivers searchip
SBIN_SCRIPTS = apt_daemon aptevent_daemon portal_xmlrpc apt_checkup \
portal_monitor apt_scheduler portal_resources \
manage_licenses manage_aggregate powder_shutdown
manage_licenses manage_aggregate powder_shutdown \
rfmonitor_daemon
LIB_SCRIPTS = APT_Profile.pm APT_Instance.pm APT_Dataset.pm APT_Geni.pm \
APT_Aggregate.pm APT_Utility.pm APT_Rspec.pm
WEB_BIN_SCRIPTS = webmanage_profile webmanage_instance webmanage_dataset \
......
This diff is collapsed.
#!/usr/bin/perl -w
#
# Copyright (c) 2000-2018 University of Utah and the Flux Group.
# Copyright (c) 2000-2019 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
......@@ -693,6 +693,7 @@ sub TBDB_PHYSICAL_NODE_TABLES() {
'interfaces' => [ 'node_id' ],
'interface_settings' => [ 'node_id' ],
'interface_state' => [ 'node_id' ],
'interfaces_rf_limit' => [ 'node_id' ],
'last_reservation' => [ 'node_id' ],
'linkdelays' => [ 'node_id' ],
'location_info' => [ 'node_id' ],
......@@ -705,6 +706,7 @@ sub TBDB_PHYSICAL_NODE_TABLES() {
'node_idlestats' => [ 'node_id' ],
'node_status' => [ 'node_id' ],
'node_rusage' => [ 'node_id' ],
'node_rf_reports' => [ 'node_id' ],
'nodeipportnum' => [ 'node_id' ],
'nodes' => [ 'node_id', 'phys_nodeid' ],
'nodeuidlastlogin' => [ 'node_id' ],
......
......@@ -3759,6 +3759,12 @@ sub RemovePhysicalState($;$)
"where ($clause) and ".
" role='" . TBDB_IFACEROLE_EXPERIMENT() . "' ")
or $errors++;
# RF interfaces (also cleaned in nfree).
DBQueryWarn("DELETE FROM interfaces_rf_limit WHERE $clause")
or $errors++;
DBQueryWarn("delete from node_rf_reports where $clause")
or $errors++;
}
foreach my $table (keys(%physicalTables)) {
......
......@@ -4575,5 +4575,50 @@ sub NeedsAdminMFS($)
return 0;
}
#
# Spectrum stuff.
#
sub AddSpectrum($$$)
{
my ($self, $spectrum, $iface) = @_;
my $node_id = $self->node_id();
my @ifaces = ();
require EmulabConstants;
if (defined($iface)) {
@ifaces = ($iface);
}
else {
if (Interface->LookupAll($self, \@ifaces)) {
return -1;
}
}
foreach $iface (@ifaces) {
my $iface_id = $iface->iface();
next
if ($iface->role() != EmulabConstants::TBDB_IFACEROLE_EXPERIMENT());
# Check to see if this is an OTA interface.
my $ota;
next
if (! ($iface->TypeCapability("overtheair", \$ota) == 0 && $ota));
foreach my $request (@{$spectrum}) {
my $frequency_low = DBQuoteSpecial($request->{"frequency_low"});
my $frequency_high = DBQuoteSpecial($request->{"frequency_high"});
my $power = DBQuoteSpecial($request->{"power"});
return -1
if (!DBQueryWarn("insert into interfaces_rf_limit set ".
" node_id='$node_id', iface='$iface_id', ".
" freq_low=$frequency_low, ".
" freq_high=$frequency_high, ".
" power=$power"));
}
}
return 0;
}
# _Always_ make sure that this 1 is at the end of the file...
1;
#!/usr/bin/perl -w
#
# Copyright (c) 2000-2017 University of Utah and the Flux Group.
# Copyright (c) 2000-2017, 2019 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
......@@ -551,6 +551,12 @@ foreach my $node (@freed_nodes) {
DBQueryWarn("delete from interface_settings where node_id='$node_id'")
or $error++;
# Clean the RF limit/reporting tables.
DBQueryWarn("delete from interfaces_rf_limit where node_id='$node_id'")
or $error++;
DBQueryWarn("delete from node_rf_reports where node_id='$node_id'")
or $error++;
# If it's a robot, we need to reset its physical location.
my $result =
DBQueryFatal("select building,floor,loc_x,loc_y,orientation ".
......
......@@ -936,6 +936,19 @@ sub GetTicketAuxAux($)
$virtexperiment->delay_osname($delayname);
}
#
# Check global spectrum request
#
my $spectrum = GeniXML::GetSpectrum($rspec);
if (@$spectrum) {
print STDERR "Global spectrum:\n";
print STDERR Dumper($spectrum);
my $response = CheckSpectrum($spectrum);
return $response
if (GeniResponse::IsError($response));
}
#
# Add global vtypes.
#
......@@ -1217,6 +1230,19 @@ sub GetTicketAuxAux($)
my $xensettings;
my $dockersettings;
my $fwsettings;
#
# Check spectrum request
#
my $spectrum = GeniXML::GetSpectrum($ref);
if (@$spectrum) {
print STDERR "Node $node_nickname spectrum:\n";
print STDERR Dumper($spectrum);
my $response = CheckSpectrum($spectrum);
goto bad
if (GeniResponse::IsError($response));
}
# Always populate iface2node mapping, even if we let the node
# pass through.
......@@ -1231,6 +1257,27 @@ sub GetTicketAuxAux($)
goto bad;
}
$iface2node{$virtual_id} = $node_nickname;
#
# Check spectrum request
#
my $spectrum = GeniXML::GetSpectrum($linkref);
if (@$spectrum) {
my $component_id = GeniXML::GetNodeId($linkref);
if (!defined($component_id)) {
$response =
GeniResponse->Create(GENIRESPONSE_BADARGS, undef,
"Must supply component_id (interface) for ".
"spectrum on $virtual_id");
goto bad;
}
print STDERR "Interface $virtual_id:$component_id spectrum:\n";
print STDERR Dumper($spectrum);
my $response = CheckSpectrum($spectrum);
goto bad
if (GeniResponse::IsError($response));
}
}
# Let remote nodes pass through.
......@@ -2786,7 +2833,9 @@ sub GetTicketAuxAux($)
#
# Look for shared and tagging attributes for the link.
#
if (my $shared_vlan = GeniXML::GetSharedLanName($linkref)) {
my $shared_vlan;
if ($shared_vlan = GeniXML::GetSharedLanName($linkref)) {
if ($shared_vlan !~ /^[-\w]*$/) {
$response =
GeniResponse->Create(GENIRESPONSE_BADARGS, undef,
......@@ -2832,7 +2881,7 @@ sub GetTicketAuxAux($)
$isshared = 1;
$encap = "vlan";
}
elsif (my $shared_vlan = GeniXML::CreateSharedLan($linkref)) {
elsif ($shared_vlan = GeniXML::CreateSharedLan($linkref)) {
if ($shared_vlan !~ /^[-\w]*$/) {
$response =
GeniResponse->Create(GENIRESPONSE_BADARGS, undef,
......@@ -4729,8 +4778,10 @@ sub SliverWorkAux($)
}
}
my %phys2nickname = ();
# global spectrum for below.
my $global_spectrum = GeniXML::GetSpectrum($rspec);
my %phys2nickname = ();
#
# Make sure all nodes requested are allocated.
#
......@@ -5159,6 +5210,49 @@ sub SliverWorkAux($)
}
}
#
# Per-Interface Spectrum
#
foreach my $ifaceref (GeniXML::FindNodes("n:interface",
$ref)->get_nodelist()) {
my $spectrum = GeniXML::GetSpectrum($ifaceref);
if (@$spectrum) {
my $component_id = GeniXML::GetNodeId($ifaceref);
my $interface = Interface->LookupByIface($node, $component_id);
if (!defined($interface) ) {
$message = "Undefined interface $component_id for ".
"spectrum on $virtual_id";
goto bad;
}
if ($node->AddSpectrum($spectrum, $interface)) {
$message = "Could not add interface spectrum to ".
"$virtual_id:$component_id";
goto bad;
}
}
}
#
# Per-node spectrum.
#
my $spectrum = GeniXML::GetSpectrum($ref);
if (@$spectrum) {
if ($node->AddSpectrum($spectrum)) {
$message = "Could not add spectrum to $virtual_id";
goto bad;
}
}
#
# And the global spectrum
#
if (@$global_spectrum) {
if ($node->AddSpectrum($global_spectrum)) {
$message = "Could not add global spectrum to $virtual_id";
goto bad;
}
}
my $node_manifest = $sliver->AnnotateManifest();
if (! defined($node_manifest)) {
$message = "Could not annotate sliver for $virtual_id";
......@@ -5529,6 +5623,7 @@ sub SliverWorkAux($)
goto bad;
}
}
# Initial update of the manifest.
$ifaceref = $sliver->AnnotateManifest($ifaceref);
my $outref;
......@@ -9255,5 +9350,60 @@ sub CheckForDeprecatedImages($$)
return 0;
}
#
# Do some sanity checking on spectrum list.
#
# Initial limits to apply to all requests, eventually these will come from
# the database. Express these as a set of ranges with associated max power,
# which for now is the same for all frequencies. In MHZ and dBm.
#
my @TXlimits = (
# freq_low,freq_high,power
[902, 928, 15],
[2400, 2500, 15],
[5725, 5875, 15],
);
sub CheckSpectrum($)
{
my ($spectrum) = @_;
foreach my $request (@{$spectrum}) {
my $frequency_low = $request->{"frequency_low"};
my $frequency_high = $request->{"frequency_high"};
my $power = $request->{"power"};
if (!defined($frequency_high) ||
!defined($frequency_low) ||
!defined($power) ||
$frequency_high !~ /^[\d\.]+$/ ||
$frequency_low !~ /^[\d\.]+$/ ||
$power !~ /^[-\d\.]+$/ ||
$frequency_low > $frequency_high ||
$frequency_low < 0) {
return GeniResponse->Create(GENIRESPONSE_BADARGS, undef,
"Invalid spectrum request");
}
# Check for out of range,power.
my $allowed = 0;
foreach my $ref (@TXlimits) {
my ($low,$high,$pmax) = @{$ref};
if ($frequency_low >= $low &&
$frequency_high <= $high &&
$power <= $pmax) {
$allowed = 1;
last;
}
}
if (!$allowed) {
return GeniResponse->Create(GENIRESPONSE_BADARGS, undef,
"Spectrum range request is out of permitted range: ".
"$frequency_low,$frequency_high,$power");
}
}
return 0;
}
# _Always_ make sure that this 1 is at the end of the file...
1;
......@@ -1944,5 +1944,20 @@ sub GetRepositories($)
return $result;
}
sub GetSpectrum($)
{
my ($ref) = @_;
my @result = ();
foreach my $element (FindNodesNS("n:spectrum", $ref,
$EMULAB_NS)->get_nodelist()) {
push(@result, {
"frequency_low" => GetText("frequency_low", $element),
"frequency_high" => GetText("frequency_high", $element),
"power" => GetText("power", $element)});
}
return \@result;
}
# _Always_ make sure that this 1 is at the end of the file...
1;
......@@ -2898,8 +2898,9 @@ DROP TABLE IF EXISTS `node_rf_reports`;
CREATE TABLE `node_rf_reports` (
`node_id` varchar(32) NOT NULL DEFAULT '',
`tstamp` datetime NOT NULL default '0000-00-00 00:00:00',
`which` enum('system','user') default 'user',
`report` text NOT NULL,
PRIMARY KEY (`node_id`)
PRIMARY KEY (`node_id`,`which`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
--
......
use strict;
use libdb;
sub DoUpdate($$$)
{
my ($dbhandle, $dbname, $version) = @_;
if (!DBSlotExists("node_rf_reports", "which")) {
DBQueryFatal("drop table node_rf_reports");
}
if (!DBTableExists("node_rf_reports")) {
DBQueryFatal("CREATE TABLE `node_rf_reports` ( ".
" `node_id` varchar(32) NOT NULL DEFAULT '', ".
" `tstamp` datetime NOT NULL default '0000-00-00 00:00:00',".
" `which` enum('system','user') default 'user', ".
" `report` text NOT NULL, ".
" PRIMARY KEY (`node_id`,`which`) ".
") ENGINE=MyISAM DEFAULT CHARSET=latin1");
}
return 0;
}
1;
# Local Variables:
# mode:perl
# End:
<?php
#
# Copyright (c) 2000-2018 University of Utah and the Flux Group.
# Copyright (c) 2000-2019 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
......@@ -254,7 +254,7 @@ function Do_ProfileList()
function Do_MemberList()
{
global $this_user, $target_group;
global $newTrustMap;
global $newTrustMap, $TB_PROJECT_ADDUSER;
if (CheckPageArgs()) {
return;
......@@ -265,6 +265,10 @@ function Do_MemberList()
$memberlist = $target_group->MemberList(0, 1);
$nonmemberlist = $target_group->NonMemberList();
$leader = $target_group->GetLeader();
# Canapprove is a proxy for group_root or better.
$statsokay = (ISADMIN() ||
$target_group->AccessCheck($this_user,
$TB_PROJECT_ADDUSER));
foreach ($memberlist as $user) {
$membership = $target_group->MemberShipInfo($user);
......@@ -291,6 +295,13 @@ function Do_MemberList()
(ISADMIN() ||
$target_group->CanDeleteUser($this_user, $user) ? 1 : 0);
if ($statsokay) {
list($pcount, $phours) = Instance::MonthsUsage($user,$target_group);
if ($phours) {
$blob["usage"] = array("pcount" => $pcount,
"phours" => intval($phours));
}
}
$members[$user->uid()] = $blob;
}
foreach ($nonmemberlist as $user) {
......
......@@ -664,7 +664,7 @@ class Instance
#
# Usage over the last months Just phours, cause pcount is not very useful.
#
function MonthsUsage($target) {
function MonthsUsage($target, $group = null) {
$monthago = time() - (3600 * 24 * 28);
$pcount = 0;
$phours = 0;
......@@ -673,6 +673,14 @@ class Instance
if (get_class($target) == "User") {
$user_idx = $target->idx();
$clause = "creator_idx='$user_idx'";
#
# Optional group target for user.
#
if ($group) {
$pid = $group->pid();
$gid = $group->gid();
$clause .= " and pid='$pid' and gid='$gid' ";
}
}
else {
$pid_idx = $target->pid_idx();
......
<?php
#
# Copyright (c) 2000-2018 University of Utah and the Flux Group.
# Copyright (c) 2000-2019 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
......@@ -133,15 +133,20 @@ function Do_ClassicDatasetList()
function Do_MemberList()
{
global $this_user, $target_project;
global $newTrustMap;
global $newTrustMap, $TB_PROJECT_ADDUSER;
if (CheckPageArgs()) {
return;
}
$target_idx = $target_project->pid_idx();
$target_pid = $target_project->pid();
$target_group= $target_project->DefaultGroup();
$results = array();
$members = $target_project->MemberList();
# Canapprove is a proxy for group_root or better.
$statsokay = (ISADMIN() ||
$target_project->AccessCheck($this_user,
$TB_PROJECT_ADDUSER));
foreach ($members as $user) {
$membership = $target_project->MemberShipInfo($user);
......@@ -159,6 +164,14 @@ function Do_MemberList()
(ISADMIN() ||
$target_project->CanDeleteUser($this_user, $user) ? 1 : 0);
if ($statsokay) {
list($pcount, $phours) = Instance::MonthsUsage($user,
$target_group);
if ($phours) {
$blob["usage"] = array("pcount" => $pcount,
"phours" => intval($phours));
}
}
$results[$user->uid()] = $blob;
}
SPITAJAX_RESPONSE($results);
......
......@@ -6,6 +6,15 @@
<th>Name</th>
<th>Email</th>
<th>Joined</th>
<% if (canapprove) { %>
<th>Usage
<span data-toggle="popover"
data-trigger="hover"
data-content="Number of node hours used in the last month."
class='glyphicon glyphicon-question-sign'
style='margin-bottom: 4px;'></span>
</th>
<% } %>
<th class="sorter-false">Privs
<a href='#' class='btn btn-xs' data-toggle='modal'
data-target='#privs-help-modal'>
......@@ -41,6 +50,15 @@
<% if (!value.approved) { %>text-danger<% } %>">
<%- value.joined %></span>
</td>
<% if (canapprove) { %>
<td>
<% if (_.has(value, "usage")) { %>
<%- value.usage.phours %>
<% } else { %>
0
<% } %>
</td>
<% } %>
<% if ((canedit || canapprove) &&
value.trust != "leader" && value.candelete) { %>
<% var trustvalues = ["none", "user","root","manager"]; %>
......