Commit 97c85e69 authored by Leigh Stoller's avatar Leigh Stoller

Various small changes to make sure that geni-lib scripts maintain a

consistent ordering from one run to the next, so that we can apply
simple diffing in rtecheck.
parent c9a4f2c3
......@@ -76,9 +76,11 @@ sub new($$)
}
my $self = {
"nodes" => {},
"nodelist" => [],
"ifaces" => {},
"links" => {},
"toplevel_elements" => {},
"linklist" => [],
"toplevel_elements" => [], # Use attributes for toplevel elements
"tour" => undef,
"namespaces" => \%namespaces,
"bscounter" => 0,
......@@ -100,6 +102,8 @@ sub new($$)
/^description$/i && do {
my $type = GeniXML::FindAttr("type", $child);
my $text = $child->textContent();
# Kill terminating newline for rtecheck
chomp($text);
$text =~ s/\"/\\"/g;
$tour->{'description'} = {
"text" => $text,
......@@ -110,11 +114,16 @@ sub new($$)
/^instructions$/i && do {
my $type = GeniXML::FindAttr("type", $child);
my $text = $child->textContent();
$text =~ s/\"/\\"/g;
$tour->{'instructions'} = {
"text" => $text,
"type" => $type,
};
# Kill terminating newline for rtecheck
chomp($text);
# This happens a lot.
if ($text ne "") {
$text =~ s/\"/\\"/g;
$tour->{'instructions'} = {
"text" => $text,
"type" => $type,
};
}
last SWITCH;
};
fatal("Unknown element in rspec_tour: $name\n");
......@@ -138,18 +147,17 @@ sub new($$)
SWITCH: for (lc($name)) {
/^password$/i && do {
my $name = GetTextOrFail("name", $ref);
$self->toplevel_elements()->{"password"} = {"name" => $name};
APT_Rspec::Attribute->new($self, $name, {"name" => $name});
last SWITCH;
};
/^routable_pool$/i && do {
$self->addPool(APT_Rspec::Pool->new($ref));
APT_Rspec::Attribute->new($self, $name,
APT_Rspec::Pool->new($ref));
last SWITCH;
};
/^collocate_factor$/i && do {
my $count = GetTextOrFail("count", $ref);
$self->toplevel_elements()->{"collocate_factor"} = {
"count" => $count
};
APT_Rspec::Attribute->new($self, $name, $count);
last SWITCH;
};
/^packing_strategy$/i && do {
......@@ -157,21 +165,17 @@ sub new($$)
if ($strategy !~ /^(pack|balance)$/) {
fatal("packing_strategy: bad strategy: $strategy");
}
$self->toplevel_elements()->{"packing_strategy"} = {
"strategy" => $strategy
};
APT_Rspec::Attribute->new($self, $name, $strategy);
last SWITCH;
};
/^routing_style$/i && do {
my $style = GetTextOrFail("style", $ref);
$self->toplevel_elements()->{"routing_style"} = {
"style" => $style
};
APT_Rspec::Attribute->new($self, $name, $style);
last SWITCH;
};
/^delay_image$/i && do {
my $urn = GetTextOrFail("urn", $ref);
$self->toplevel_elements()->{"delay_image"} = {"urn" => $urn};
APT_Rspec::Attribute->new($self, $name, $urn);
last SWITCH;
};
fatal("Toplevel XML element $name is not supported");
......@@ -250,9 +254,12 @@ sub new($$)
}
sub nodes($) { return $_[0]->{'nodes'}; }
sub links($) { return $_[0]->{'links'}; }
sub nodelist($) { return $_[0]->{'nodelist'}; }
sub linklist($) { return $_[0]->{'linklist'}; }
sub ifaces($) { return $_[0]->{'ifaces'}; }
sub tour($) { return $_[0]->{'tour'}; }
sub toplevel_elements($) { return $_[0]->{'toplevel_elements'}; }
sub attributes($) { return $_[0]->{'toplevel_elements'}; }
sub toplevel_elements($) { return $_[0]->attributes(); }
sub description($)
{
......@@ -276,6 +283,8 @@ sub instructions($)
sub addNode($$)
{
my ($self, $node) = @_;
# Maintain rspec ordering for RTE checks.
push(@{$self->nodelist()}, $node);
$self->nodes()->{$node->client_id()} = $node;
}
sub getNode($$)
......@@ -302,6 +311,8 @@ sub getIface($$)
sub addLink($$)
{
my ($self, $link) = @_;
# Maintain rspec ordering for RTE checks.
push(@{$self->linklist()}, $link);
$self->links()->{$link->client_id()} = $link;
}
sub getLink($$)
......@@ -312,20 +323,6 @@ sub getLink($$)
return $self->{'links'}->{$client_id};
}
sub addPool($$)
{
my ($self, $pool) = @_;
#
# We can have more then one pool.
#
if (!exists($self->toplevel_elements()->{"routable_pool"})) {
$self->toplevel_elements()->{"routable_pool"} = {};
}
my $client_id = $pool->client_id();
$self->toplevel_elements()->{"routable_pool"}->{$client_id} = $pool;
return $pool;
}
#
# Not sure what I want to do about errors.
......@@ -514,7 +511,7 @@ sub Compare($$)
if (0 && APT_Rspec::CompareHashes($key, $val1, $val2));
last SWITCH;
};
/^(bscounter|namespaces)$/i && do {
/^(bscounter|namespaces|nodelist|linklist)$/i && do {
last SWITCH;
};
print STDERR "Rspec:Compare: Unknown attribute: $key\n";
......@@ -525,15 +522,22 @@ sub Compare($$)
}
#
# Compare top level elements.
# Compare top level elements. These are lists.
#
sub CompareToplevelElements($$)
{
my ($elements1, $elements2) = @_;
foreach my $key (keys(%{$elements1})) {
my $val1 = $elements1->{$key};
my $val2 = $elements1->{$key};
if (scalar(@{$elements2}) != scalar(@{$elements2})) {
print STDERR "Rspec:CompareToplevelElements: mismatching length\n";
return -1;
}
for (my $index = 0; $index < scalar(@{$elements1}); $index++) {
my $attr1 = @{$elements1}[$index];
my $attr2 = @{$elements2}[$index];
my $key = $attr1->name();
my $val1 = $attr1->value();
my $val2 = $attr2->value();
#
# Enumerate all fields to be certain this is not out of sync
......@@ -549,7 +553,7 @@ sub CompareToplevelElements($$)
(/^(password|collocate_factor|packing_strategy)$/i ||
/^(routing_style|delay_image)$/i) && do {
return 1
if (APT_Rspec::CompareHashes($key, $val1, $val2));
if (APT_Rspec::CompareValues($key, $val1, $val2));
last SWITCH;
};
print STDERR "Rspec:CompareToplevelElements: unknown $key\n";
......@@ -690,6 +694,32 @@ sub CompareLinks($$)
return 0;
}
############################################################################
# The point of this class is provide a consistent way to describe all
# the little attributes in elements, and maintain their order so that
# we can generate the exact same rspec/genilib script each time.
#
package APT_Rspec::Attribute;
use Carp;
use English;
use GeniXML;
use GeniHRN;
sub new($$$$)
{
my ($class, $parent, $name, $value) = @_;
my $self = {
"name" => $name,
"value" => $value,
};
bless($self, $class);
push(@{$parent->attributes()}, $self);
return $self;
}
sub name($) { return $_[0]->{'name'}; }
sub value($) { return $_[0]->{'value'}; }
############################################################################
package APT_Rspec::Node;
use Carp;
......@@ -717,6 +747,7 @@ sub new($$$$$)
"statements" => [],
"desires" => {},
"ifaces" => {},
"ifacelist" => [],
"blockstores" => {},
"pipes" => {},
"use_type_default_image"=> undef,
......@@ -732,6 +763,7 @@ sub exclusive($) { return $_[0]->{'exclusive'}; }
sub statements($) { return $_[0]->{'statements'}; }
sub blockstores($) { return $_[0]->{'blockstores'}; }
sub ifaces($) { return $_[0]->{'ifaces'}; }
sub ifacelist($) { return $_[0]->{'ifacelist'}; }
sub services($) { return $_[0]->{'services'}; }
sub pipes($) { return $_[0]->{'pipes'}; }
sub tag($) { return $_[0]->{'tag'}; }
......@@ -758,6 +790,7 @@ sub addBridgePipe($$) {
sub addIface($$)
{
my ($self, $iface) = @_;
push(@{$self->ifacelist()}, $iface);
$self->ifaces()->{$iface->client_id()} = $iface;
}
sub addService($$)
......@@ -778,6 +811,9 @@ sub addNode($$$)
my $client_id = GeniXML::GetVirtualId($noderef);
my $sliver_type = GeniXML::GetVirtualizationSubtype($noderef);
my $exclusive = GeniXML::GetExclusive($noderef);
if (defined($exclusive)) {
$exclusive = ($exclusive ? 1 : 0);
}
fatal("Node: missing client_id or sliver_type")
if (! defined($client_id));
......@@ -1105,7 +1141,7 @@ sub Compare($$)
$val1,$val2));
last SWITCH;
};
/^ifaces$/i && do {
/^(ifaces|ifacelist)$/i && do {
# Handled up above in CompareNodes.
last SWITCH;
};
......@@ -1240,6 +1276,7 @@ sub new($$$$)
"isbslink" => 0,
"bsnode" => undef,
"ifaces" => {},
"ifacelist" => [],
"properties" => {},
"component_managers" => [],
"jacks_site" => undef,
......@@ -1253,6 +1290,7 @@ sub client_id($) { return $_[0]->{'client_id'}; }
sub type($) { return $_[0]->{'type'}; }
sub statements($) { return $_[0]->{'statements'}; }
sub ifaces($) { return $_[0]->{'ifaces'}; }
sub ifacelist($) { return $_[0]->{'ifacelist'}; }
sub properties($) { return $_[0]->{'properties'}; }
sub tag($) { return $_[0]->{'tag'}; }
sub fatal($) { return APT_Rspec::fatal($_[0]); }
......@@ -1273,6 +1311,7 @@ sub addIface($$)
{
my ($self, $iface) = @_;
my $iface_id = $iface->client_id();
push(@{$self->ifacelist()}, $iface);
$self->ifaces()->{$iface_id} = $iface;
}
......@@ -1541,7 +1580,7 @@ sub Compare($$)
# See below.
last SWITCH;
};
/^ifaces$/i && do {
/^(ifaces|ifacelist)$/i && do {
# Handled up in CompareLinks()
last SWITCH;
};
......
......@@ -42,12 +42,14 @@ sub usage()
print STDERR " -d - Turn on debugging\n";
print STDERR " -o file - Specify output file for geni-lib\n";
print STDERR " -s file - Specify output file for post geni-lib rspec\n";
print STDERR " -r - Regression test (run geni-lib, compare rspecs)\n";
print STDERR " -r - Regression test; run geni-lib, compare rspecs\n";
print STDERR " -t - Do not spit out the Tour (for regression)\n";
exit(-1);
}
my $optlist = "do:rs:";
my $optlist = "do:rs:t";
my $debug = 0;
my $regress = 0;
my $notour = 0;
my $ofile;
my $rfile;
......@@ -101,6 +103,9 @@ if (! getopts($optlist, \%options)) {
if (defined($options{"d"})) {
$debug = 1;
}
if (defined($options{"t"})) {
$notour = 1;
}
if (defined($options{"o"})) {
$ofile = $options{"o"};
}
......@@ -141,7 +146,7 @@ sub GenerateNodeStatements($)
{
my ($rspec) = @_;
foreach my $node (values(%{$rspec->nodes()})) {
foreach my $node (@{$rspec->nodelist()}) {
my $client_id = $node->client_id();
my $ntag = $node->tag();
my $ntype = $node->type();
......@@ -151,11 +156,19 @@ sub GenerateNodeStatements($)
#
if ($ntype eq "emulab-xen") {
$node->addStatement("$ntag = request.XenVM('$client_id')");
#
# This is the only time we need to spit this out, since
# the default is False.
#
if (defined($node->{'exclusive'}) && $node->{'exclusive'}) {
$node->addTagStatement("exclusive = True");
}
if (defined($node->{'xen_settings'})) {
my $settings = $node->{'xen_settings'};
foreach my $setting (keys(%{$settings})) {
foreach my $setting (sort(keys(%{$settings}))) {
my $value = $settings->{$setting};
if ($setting eq "ram") {
$node->addTagStatement("ram = $value");
......@@ -191,7 +204,9 @@ sub GenerateNodeStatements($)
#
# And the pipes. Just two of them.
#
foreach my $p (values(%{$node->pipes()})) {
foreach my $k (sort(keys(%{$node->pipes()}))) {
my $p = $node->pipes()->{$k};
my $pname = ($p->{'iface_id'} eq $if0
? "pipe('$if0')" : "pipe('$if1')");
......@@ -232,10 +247,6 @@ sub GenerateNodeStatements($)
#
# Attributes and flags.
#
if (defined($node->{'exclusive'})) {
my $exclusive = ($node->{'exclusive'} ? "True" : "False");
$node->addTagStatement("exclusive = $exclusive");
}
if (defined($node->{'component_id'})) {
my $component_id = $node->{'component_id'};
$node->addTagStatement("component_id = '$component_id'");
......@@ -277,6 +288,7 @@ sub GenerateNodeStatements($)
/^execute$/i && do {
my $shell = $service->{'shell'};
my $cmd = $service->{'cmd'};
$cmd =~ s/\'/\\'/g;
$node->addTagStatement(
"addService(pg.Execute('$shell','$cmd'))");
last SWITCH;
......@@ -302,7 +314,7 @@ sub GenerateNodeStatements($)
#
# Desires.
#
foreach my $desire (keys(%{ $node->{'desires'} })) {
foreach my $desire (sort(keys(%{ $node->{'desires'} }))) {
my $weight = $node->{'desires'}->{$desire};
$node->addTagStatement("Desire('$desire','$weight')");
}
......@@ -310,9 +322,8 @@ sub GenerateNodeStatements($)
#
# Interfaces.
#
foreach my $id (sort(keys(%{$node->{'ifaces'}}))) {
my $iface = $node->{'ifaces'}->{$id};
my $client_id = $iface->{'client_id'};
foreach my $iface (@{$node->ifacelist()}) {
my $iface_id = $iface->{'client_id'};
my $itag = $iface->{'tag'};
my $ip = $iface->{'ip'};
my $mask = $iface->{'mask'};
......@@ -321,9 +332,15 @@ sub GenerateNodeStatements($)
# geni-lib is going to name the interfaces as node_id:iface_id
# so we have to careful to strip existing node_id from the id
# in case the rspec came from a geni-lib script. But geni-lib
# will not prepend the node_id if it is already in : format.
# will not prepend the node_id if it is already in : format,
# so if the user named it whacky:iface_id, we are going to
# use that.
#
my $iface_id = $client_id;
if ($iface_id =~ /^([^:]*):(.*)$/) {
if ($1 eq $client_id) {
$iface_id = $2;
}
}
my $addr = ($ip && $mask ? "pg.IPv4Address('$ip','$mask')" : undef);
#
......@@ -368,7 +385,8 @@ sub GenerateNodeStatements($)
if (defined($size)) {
$node->addStatement("${btag}.size = '$size'");
}
if (defined($placement)) {
# Do not not spit out default.
if (defined($placement) && $placement ne "any") {
$node->addStatement("${btag}.placement = '$placement'");
}
if (defined($dataset)) {
......@@ -381,7 +399,7 @@ sub SpitNodeStatements($$)
{
my ($rspec, $where) = @_;
foreach my $node (values(%{$rspec->nodes()})) {
foreach my $node (@{$rspec->nodelist()}) {
my $client_id = $node->client_id();
print $where "# Node $client_id\n";
foreach my $statement (@{$node->statements()}) {
......@@ -398,7 +416,7 @@ sub GenerateLinkStatements($)
{
my ($rspec) = @_;
foreach my $link (values(%{$rspec->links()})) {
foreach my $link (@{$rspec->linklist()}) {
my $client_id = $link->client_id();
my $ltag = $link->tag();
my $ltype = $link->type();
......@@ -412,12 +430,10 @@ sub GenerateLinkStatements($)
"$ltag = request.Link('$client_id', 'vlan')");
}
elsif ($ltype eq "egre-tunnel") {
$link->addStatement("$ltag = pg.L2GRE('$client_id')");
$link->addStatement("request.addResource($ltag)");
$link->addStatement("$ltag = request.L2GRE('$client_id')");
}
elsif ($ltype eq "gre-tunnel") {
$link->addStatement("$ltag = pg.L3GRE('$client_id')");
$link->addStatement("request.addResource($ltag)");
$link->addStatement("$ltag = request.L3GRE('$client_id')");
}
else {
fatal("Unknown link type $ltype for $client_id");
......@@ -486,7 +502,7 @@ sub GenerateLinkStatements($)
#
# Shaping.
#
foreach my $key (keys(%{$link->properties()})) {
foreach my $key (sort(keys(%{$link->properties()}))) {
my $property = $link->{'properties'}->{$key};
my $dest = $property->{'dest'};
my $iface = $rspec->getIface($property->source());
......@@ -499,8 +515,7 @@ sub GenerateLinkStatements($)
}
}
foreach my $id (sort(keys(%{$link->{'ifaces'}}))) {
my $iface = $link->{'ifaces'}->{$id};
foreach my $iface (@{$link->ifacelist()}) {
my $client_id = $iface->{'client_id'};
my $itag = $iface->{'tag'};
......@@ -515,7 +530,7 @@ sub SpitLinkStatements($$)
{
my ($rspec, $where) = @_;
foreach my $link (values(%{$rspec->links()})) {
foreach my $link (@{$rspec->linklist()}) {
my $client_id = $link->client_id();
print $where "# Link $client_id\n";
foreach my $statement (@{$link->statements()}) {
......@@ -529,44 +544,38 @@ sub SpitTopLevelStatements($$)
{
my ($rspec, $where) = @_;
foreach my $name (keys(%{$rspec->toplevel_elements()})) {
my $val = $rspec->toplevel_elements()->{$name};
foreach my $attribute (@{$rspec->toplevel_elements()}) {
my $name = $attribute->name();
my $val = $attribute->value();
SWITCH: for (lc($name)) {
/^password$/i && do {
my $name = $val->{'name'};
print $where "password = emulab.Password('$name')\n";
print $where "password = emulab.Password('$val')\n";
print $where "request.addResource(password)\n";
last SWITCH;
};
/^routable_pool$/i && do {
foreach my $pool (values(%{$val})) {
my $name = $pool->client_id();
my $count = $pool->count();
my $type = $pool->type();
print $where
"request.AddressPool('$name', $count, '$type')\n";
}
my $pool = $val;
my $id = $pool->client_id();
my $count = $pool->count();
my $type = $pool->type();
print $where "request.AddressPool('$id', $count, '$type')\n";
last SWITCH;
};
/^collocate_factor$/i && do {
my $count = $val->{'count'};
print $where "request.setCollocateFactor($count)\n";
print $where "request.setCollocateFactor($val)\n";
last SWITCH;
};
/^packing_strategy$/i && do {
my $strategy = $val->{"strategy"};
print $where "request.setPackingStrategy('$strategy')\n";
print $where "request.setPackingStrategy('$val')\n";
last SWITCH;
};
/^routing_style$/i && do {
my $style = $val->{"style"};
print $where "request.setRoutingStyle('$style')\n";
print $where "request.setRoutingStyle('$val')\n";
last SWITCH;
};
/^delay_image$/i && do {
my $urn = $val->{"urn"};
print $where "request.setDelayImage('$urn')\n";
print $where "request.setDelayImage('$val')\n";
last SWITCH;
};
fatal("toplevel element $name is not supported");
......@@ -676,7 +685,7 @@ if (!defined($outfd)) {
fatal("Could not open temporary file for result rspec");
return -1;
}
SpitTour($rspec, $outfd);
SpitTour($rspec, $outfd) if (!$notour);
SpitPreamble($outfd);
SpitNodeStatements($rspec, $outfd);
SpitLinkStatements($rspec, $outfd);
......
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