Commit 78969baa authored by Jonathon Duerig's avatar Jonathon Duerig
Browse files

Complete rewrite of ptopgen from the ground up to use db/* classes. Checkpoint.

Basic framework is in place. Availability and printability is codified into
functions now. Node.pm modified so that lookups can be done in bulk for
efficiency. When making XML, we now create XML trees rather than printing
out XML fragments.
parent 98eb7972
...@@ -168,10 +168,19 @@ sub Lookup($$) ...@@ -168,10 +168,19 @@ sub Lookup($$)
return undef return undef
if (!$query_result || !$query_result->numrows); if (!$query_result || !$query_result->numrows);
return LookupRow($class, $query_result->fetchrow_hashref());
}
#
# Lookup a (physical) node based on an existing row from the database.
# Useful for bulk lookups.
#
sub LookupRow($$)
{
my ($class, $hash_orig) = @_;
# #
# Make a copy of the array. Still fighting memory corruption error. # Make a copy of the array. Still fighting memory corruption error.
# #
my $hash_orig = $query_result->fetchrow_hashref();
my $hash_copy = {}; my $hash_copy = {};
foreach my $key (keys(%{ $hash_orig })) { foreach my $key (keys(%{ $hash_orig })) {
my $val = $hash_orig->{$key}; my $val = $hash_orig->{$key};
...@@ -190,7 +199,7 @@ sub Lookup($$) ...@@ -190,7 +199,7 @@ sub Lookup($$)
# XXX debug # XXX debug
my $row = $self->{"DBROW"}; my $row = $self->{"DBROW"};
$nodeid = checknodeid($row->{'node_id'}, "From DB"); my $nodeid = checknodeid($row->{'node_id'}, "From DB");
# Add to cache. # Add to cache.
$nodes{$nodeid} = $self; $nodes{$nodeid} = $self;
...@@ -739,6 +748,16 @@ sub IsReserved($) ...@@ -739,6 +748,16 @@ sub IsReserved($)
return 1; return 1;
} }
#
# Set reserved member based on a database row. Useful for bulk lookups.
#
sub SetReservedRow($$)
{
my ($self, $reserved) = @_;
if ($reserved->{"node_id"} eq $self->node_id()) {
$self->{"RSRV"} = $reserved;
}
}
sub GetSubboss($$) sub GetSubboss($$)
{ {
......
...@@ -29,12 +29,17 @@ use GeniHRN; ...@@ -29,12 +29,17 @@ use GeniHRN;
use GeniUtil; use GeniUtil;
use Carp qw(cluck carp); use Carp qw(cluck carp);
use vars qw($RSPEC_0_1 $RSPEC_0_2 $RSPEC_2 $EMULAB_NS $XSI_NS $STITCH_NS use vars qw($RSPEC_0_1 $RSPEC_0_2 $RSPEC_2
$RSPEC_0_1_NS $RSPEC_0_2_NS $RSPEC_2_NS
$EMULAB_NS $XSI_NS $STITCH_NS
$REQUEST_URL $MANIFEST_URL); $REQUEST_URL $MANIFEST_URL);
$RSPEC_0_1 = "0.1"; $RSPEC_0_1 = "0.1";
$RSPEC_0_2 = "0.2"; $RSPEC_0_2 = "0.2";
$RSPEC_2 = "2"; $RSPEC_2 = "2";
$RSPEC_0_1_NS = "http://www.protogeni.net/resources/rspec/0.1";
$RSPEC_0_2_NS = "http://www.protogeni.net/resources/rspec/0.2";
$RSPEC_2_NS = "http://www.protogeni.net/resources/rspec/2";
$EMULAB_NS = "http://www.protogeni.net/resources/rspec/ext/emulab/1"; $EMULAB_NS = "http://www.protogeni.net/resources/rspec/ext/emulab/1";
$XSI_NS = "http://www.w3.org/2001/XMLSchema-instance"; $XSI_NS = "http://www.w3.org/2001/XMLSchema-instance";
$STITCH_NS = "http://hpn.east.isi.edu/rspec/ext/stitch/0.1/"; $STITCH_NS = "http://hpn.east.isi.edu/rspec/ext/stitch/0.1/";
...@@ -753,7 +758,7 @@ sub CreateDocument($$) ...@@ -753,7 +758,7 @@ sub CreateDocument($$)
{ {
my ($ns, $name) = @_; my ($ns, $name) = @_;
my $doc = XML::LibXML::Document->createDocument("1.0", "UTF-8"); my $doc = XML::LibXML::Document->createDocument("1.0", "UTF-8");
my $root = $doc->createElementNS($ns, "rs:$name"); my $root = $doc->createElementNS($ns, "$name");
$doc->setDocumentElement($root); $doc->setDocumentElement($root);
return $doc; return $doc;
} }
......
#!/usr/bin/perl -wT
#
# EMULAB-COPYRIGHT
# Copyright (c) 2010 University of Utah and the Flux Group.
# All rights reserved.
#
package libptopnew;
use strict;
use Exporter;
use lib "/usr/testbed/lib";
#use lib "@prefix@/lib";
use Node;
use libdb qw(TBGetSiteVar);
use vars qw(@ISA @EXPORT @EXPORT_OK);
my $PGENISUPPORT = 1;
my $OURDOMAIN = "jonlab.tbres.emulab.net";
my $MAINSITE = 0;
#my $PGENISUPPORT = @PROTOGENI_SUPPORT@;
#my $OURDOMAIN = "@OURDOMAIN@";
#my $MAINSITE = @TBMAINSITE@;
my $cmuuid = TBGetSiteVar('protogeni/cm_uuid');
my $cmurn = "";
if ($PGENISUPPORT) {
require GeniHRN;
require GeniXML;
$cmurn = GeniHRN::Generate($OURDOMAIN, "authority", "cm");
}
@ISA = "Exporter";
@EXPORT = qw( );
my $user_project = undef;
my $exempt_eid = undef;
my $available_only = 0;
my $print_widearea = 0;
my $print_shared = 0;
my $genimode = 1;
our %nodeList = ();
our %linkList = ();
# Table of which types the user project is allowed to have.
# Keyed by type where 1 = allowed, 0 = denied, and ! exists = allowed
our %permissions = ();
sub Init()
{
InitPermissions();
}
#
# Initialize project permissions table if the user specified a project.
#
sub InitPermissions()
{
if (defined($user_project)) {
# By default a type is allowed for every project. If a type is
# in the permissions table, it is allowed only for those
# projects which it is attached to.
my $dbresult
= DBQueryFatal("select distinct type ".
"from nodetypeXpid_permissions");
while (my ($type) = $dbresult->fetchrow_array) {
$permissions{$type} = 0;
}
$dbresult
= DBQueryFatal("select type ".
"from nodetypeXpid_permissions".
"where pid='$user_project'");
while (my ($type) = $dbresult->fetchrow_array) {
$permissions{$type} = 1;
}
}
}
sub TypeAllowed($)
{
my ($type) = @_;
return (! defined($user_project)
|| ! exists($permissions{$type})
|| $permissions{$type});
}
# Accessors
sub Nodes() { return \%nodeList; }
sub Links() { return \%linkList; }
# Add new nodes and links
sub CreateNode($)
{
my ($row) = @_;
my $node = libptop::pnode->Create($row);
Nodes()->{$node->name()} = $node;
return $node;
}
###############################################################################
# Physical Nodes. These contain the all of the per-node state used to
# generate ptop or xml files.
package libptop::pnode;
use EmulabConstants;
sub Create($$)
{
my ($class, $row) = @_;
my $self = {};
$self->{'NODE'} = Node->LookupRow($row);
$self->{'PTYPES'} = [];
bless($self, $class);
return $self;
}
# Accessors
sub name($) { return $_[0]->{'NODE'}->node_id(); }
sub node($) { return $_[0]->{'NODE'}; }
sub type($) { return $_[0]->node()->NodeTypeInfo(); }
sub available($;$)
{
my ($self, $tagRef) = @_;
my $node = $self->node();
my $reserved_pid = $node->reserved_pid();
my $reserved_eid = $node->ReservationID();
# A node is reserved to a project if it has a reserved_pid, and
# that pid is not the user's pid.
my $project_reserved = defined($reserved_pid)
&& (! defined($user_project)
|| $user_project ne $reserved_pid);
# A node is reserved to an experiment if it has a reserved_eid,
# a reserved_pid, and one of those is not the user's pid/eid.
my $exp_reserved = defined($reserved_eid)
&& defined($reserved_pid)
&& (! defined($exempt_eid)
|| $reserved_eid ne $exempt_eid
|| $reserved_pid ne $user_project);
my $isreserved = $project_reserved || $exp_reserved;
my $typeallowed = (libptopnew::TypeAllowed($node->class())
&& libptopnew::TypeAllowed($node->type()));
# Nodes are free if they are nonlocal, or if they are up and
# not-reserved, or if they are shared.
#
# And they must also be allowed for the current project by the
# nodetypeXpid_permissions table.
my $isfree = ((!$self->islocal()
|| (! $isreserved && $self->isup())
|| $self->isshared())
&& $typeallowed);
# And if they are a subnode, their parent must be available:
# A bit of recursion to ensure that subnodes are only listed as
# available if their parent is. The tags bit is to try to ensure
# we don't loop forever if there is a subnode-loop. See also willPrint().
if ($isfree && $node->issubnode()) {
my %tags = {};
if (defined($tagRef)) {
%tags = %{ $tagRef };
}
$tags{$self->name()} = 1;
my $parent = $node->phys_nodeid();
if (! exists($tags{$parent})) {
$isfree = $isfree && $nodeList{$parent}->available(\%tags);
}
}
return $isfree;
}
sub addPType($$)
{
my ($self, $newType) = @_;
push(@{ $self->{'PTYPES'} }, $newType);
}
sub processSwitch($)
{
my ($self) = @_;
if (! $self->isswitch()) {
return;
}
# Add switch and lan types
$self->addPType(libptop::pnode_type->Create("switch", 1));
if (!(defined($MAINSITE) && $MAINSITE
&& $self->name() eq "procurve1")) {
$self->addPType(libptop::pnode_type->Create("lan", undef, 1));
}
# Add real-switch feature
#my $features = ["real-switch:0"];
}
sub processLocal($)
{
my ($self) = @_;
if (! $self->islocal()) {
return;
}
}
sub processWidearea($)
{
my ($self) = @_;
if (! $self->iswidearea()) {
return;
}
}
sub isswitch($)
{
my ($self) = @_;
my $role = $self->node()->role();
return ($role eq 'testswitch' || $role eq 'widearea_switch'
|| ($role eq 'testnodefoo' && $self->node()->isswitch()));
}
sub islocal($)
{
my ($self) = @_;
my $node = $self->node();
my $isremotenode = $node->isremotenode();
my $wa_attrvalue = $node->NodeTypeAttribute('dedicated_widearea');
return ( $node->role() eq 'testnode'
&& ((! defined($isremotenode) || $isremotenode == 0)
|| (defined($wa_attrvalue) && $wa_attrvalue == 1)));
}
sub iswidearea($)
{
my ($self) = @_;
my $node = $self->node();
my $isremotenode = $node->isremotenode();
my $isvirtnode = $node-> isvirtnode();
my $wa_attrvalue = $node->NodeTypeAttribute('dedicated_widearea');
return ($node->role() eq 'testnode'
&& defined($isremotenode)
&& $isremotenode == 1
&& (! defined($isvirtnode) || $isvirtnode == 0)
&& $node->type() ne 'pcfedphys'
&& (! defined($wa_attrvalue) || $wa_attrvalue == 0));
}
sub isshared($)
{
my ($self) = @_;
my $node = $self->node();
# In shared mode, allow allocated nodes whose sharing_mode is set.
return (defined($node->erole())
&& $node->erole() eq "sharedhost"
&& $self->isup());
}
sub isup($)
{
my ($self) = @_;
my $eventstate = $self->node()->eventstate();
return defined($eventstate)
&& ($eventstate eq TBDB_NODESTATE_ISUP()
|| $eventstate eq TBDB_NODESTATE_PXEWAIT()
|| $eventstate eq TBDB_NODESTATE_POWEROFF()
|| $eventstate eq TBDB_NODESTATE_ALWAYSUP());
}
sub willPrint($;$)
{
my ($self, $tagRef) = @_;
my $node = $self->node();
# In geni mode, disallow nodes tagged protogeni_exclude from being printed.
my $geniExclude = 0;
$node->NodeAttribute("protogeni_exclude", \$geniExclude);
my $geniok = (! defined($geniExclude) || $geniExclude == 0);
my $result = ($self->isswitch()
|| $self->islocal()
|| ($self->iswidearea() && $print_widearea)
|| ($self->isshared() && $print_shared))
&& (! $available_only || $self->available($)))
&& (! $genimode || $geniok);
# A bit of recursion to ensure that subnodes are only printed if
# their parent is. The tags bit is to try to ensure we don't loop
# forever if there is a subnode-loop. See also available()).
if ($result && $node->issubnode()) {
my %tags = {};
if (defined($tagRef)) {
%tags = %{ $tagRef };
}
$tags{$self->name()} = 1;
my $parent = $node->phys_nodeid();
if (! exists($tags{$parent})) {
$result = $result && $nodeList{$parent}->willPrint(\%tags);
}
}
return $result;
}
sub toString($)
{
my ($self) = @_;
my $result = "node " . $self->name();
foreach my $type (@{ $self->{'PTYPES'} }) {
$result .= " " . $type->toString();
}
return $result;
}
sub toXML($$)
{
my ($self, $parent) = @_;
my $xml = GeniXML::AddElement("node", $parent);
my $urn = GeniHRN::Generate($OURDOMAIN, "node", $self->name());
if (GeniXML::IsVersion0($xml)) {
GeniXML::SetText("component_manager_uuid", $xml, $cmurn);
GeniXML::SetText("component_uuid", $xml, $urn);
} else {
GeniXML::SetText("component_manager_id", $xml, $cmurn);
GeniXML::SetText("component_id", $xml, $urn);
}
GeniXML::SetText("component_name", $xml, $self->name());
foreach my $type (@{ $self->{'PTYPES'} }) {
$type->toXML($xml);
}
}
###############################################################################
# Physical Node Type. These are the types which are printed out in the
# ptopgen file. Note that there is not a one-to-one correspondence
# between these types and the 'type' of the node in the database.
package libptop::pnode_type;
sub Create($$;$$)
{
my ($class, $name, $slots, $isstatic) = @_;
my $self = {};
$self->{'NAME'} = $name;
$self->{'SLOTS'} = $slots;
$self->{'ISSTATIC'} = 0;
if (defined($isstatic)) {
$self->{'ISSTATIC'} = 1;
}
bless($self, $class);
return $self;
}
# Accessors
sub name($) { return $_[0]->{'NAME'}; }
sub slots($) { return $_[0]->{'SLOTS'}; }
sub isstatic($) { return $_[0]->{'ISSTATIC'}; }
sub toString($)
{
my ($self) = @_;
my $result = "";
if ($self->isstatic()) {
$result .= "*";
}
$result .= $self->name().":";
if (defined($self->slots())) {
$result .= $self->slots();
} else {
$result .= "*";
}
return $result;
}
sub toXML($$)
{
my ($self, $parent) = @_;
my $slots = $self->slots();
if (! defined($slots)) {
$slots = "unlimited";
}
my $xml;
if (GeniXML::IsVersion0($parent)) {
$xml = GeniXML::AddElement("node_type", $parent);
GeniXML::SetText("type_name", $xml, $self->name());
GeniXML::SetText("type_slots", $xml, $slots);
if ($self->isstatic()) {
GeniXML::SetText("static", $xml, "true");
}
} else {
my $sliverxml;
if ($self->name() eq "pc") {
$sliverxml = GeniXML::AddElement("sliver_type", $parent);
GeniXML::SetText("name", $sliverxml, "raw-pc");
# TODO: osids
}
if ($self->name() eq "pcvm") {
$sliverxml = GeniXML::AddElement("sliver_type", $parent);
GeniXML::SetText("name", $sliverxml, "emulab-openvz");
}
$xml = GeniXML::AddElement("hardware_type", $parent);
GeniXML::SetText("name", $xml, $self->name());
my $elab = GeniXML::AddElement("node_type", $xml, $GeniXML::EMULAB_NS);
GeniXML::SetText("type_slots", $elab, $slots);
if ($self->isstatic()) {
GeniXML::SetText("static", $elab, "true");
}
}
}
1;
#!/usr/bin/perl -w
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2011 University of Utah and the Flux Group.
# All rights reserved.
#
# TODO: USER_COMPONENT --> No caching, willPrint, available, etc.
use strict;
use English;
use Getopt::Std;
use Math::BigInt;
use List::Util 'shuffle';
#use lib "@prefix@/lib";
use lib "/usr/testbed/lib";
use lib ".";
use libdb;
use libadminctrl;
use libptopnew;
use Experiment;
use Lan;
use Node;
use NodeType;
use GeniXML;
sub processArgs();
sub consultDatabase();
sub process();
sub printResults();
libptopnew::Init();
#
# Turn off line buffering on output
#
$| = 1;
my $print_xml = 0;
my $print_format = "CRAPPY";
my $print_ns = $GeniXML::RSPEC_2_NS;
processArgs();
consultDatabase();
printResults();
sub processArgs()
{
}
sub consultDatabase()
{
my $dbresult;
my $row;
# Bulk lookup on nodes table
$dbresult = DBQueryFatal("select * from nodes");
while ($row = $dbresult->fetchrow_hashref()) {
libptopnew::CreateNode($row);
}
# Bulk lookup on reserved table
$dbresult = DBQueryFatal("select * from reserved");
while ($row = $dbresult->fetchrow_hashref()) {
libptopnew::Nodes()->{$row->{"node_id"}}->node()->SetReservedRow($row);
}
# Process each node adding ptypes and features
foreach my $current (values(%{ libptopnew::Nodes() })) {
if ($current->willPrint()) {
$current->processSwitch();
$current->processLocal();
$current->processWidearea();
}
}
}
sub printResults()
{
my $doc = GeniXML::CreateDocument($print_ns, "rspec");
my $rspec = $doc->documentElement();
foreach my $current (values(%{ libptopnew::Nodes() })) {
if ($current->willPrint()) {
if ($print_xml) {
$current->toXML($rspec);
} else {
print $current->toString()."\n";
}
}
}
if ($print_xml) {
print GeniXML::Serialize($rspec, 1)."\n";
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment