Commit 16ce3cf8 authored by Jonathon Duerig's avatar Jonathon Duerig

New command line tool for verifying rspecs against multiple schemas.

Testing this tool also allowed me to find some typos in the delaylink example.
parent bc82c1a5
#!/usr/bin/perl -w
#use English;
use Getopt::Std;
use XML::LibXML;
use XML::LibXML::XPathContext;
use XML::LibXML::NodeList;
sub usage
{
die("Usage: rspeclint [<namespace> <schema>]+ <document>\n\n".
"Schema and document locations are either paths or URLs.\n");
}
sub addSchema($$);
sub visit($);
sub verify($);
sub report($$);
%ns2file = ();
%ns2schema = ();
#my %options;
#
#if (! getopts('', \%options)) {
# usage();
#}
while (scalar(@ARGV) > 1) {
my $ns = shift(@ARGV);
my $schema = shift(@ARGV);
addSchema($ns, $schema);
}
if (scalar(@ARGV) != 1) {
usage();
}
my $parser = XML::LibXML->new(line_numbers => 1);
my $docfile = $ARGV[0];
my $doc;
eval {
$doc = $parser->parse_file($docfile);
};
if ($@) {
die("Failed to parse XML document at location $docfile: $@\n");
}
visit($doc->documentElement());
sub addSchema($$)
{
my ($ns, $file) = @_;
if (! exists($ns2file{$ns})) {
my $schema;
eval {
$schema = XML::LibXML::Schema->new(location => $file);
};
if ($@) {
die("Failed to load schema for namespace $ns from location $file\n: $@");
}
$ns2file{$ns} = $file;
$ns2schema{$ns} = $schema;
} else {
my $exemplar = $ns2file{$ns};
if ($exemplar ne $file) {
die("Schema location mismatch on namespace $ns: Original location is $exemplar while new location is $file\n");
}
}
# print STDERR "Added Schema $ns -- $file\n";
}
sub visit($)
{
my ($node) = @_;
my $nodens = $node->namespaceURI();
if (! defined($nodens)) {
report("Element has no namespace", $node);
}
# Add any new schemaLocation pairs
my $uri = "http://www.w3.org/2001/XMLSchema-instance";
my $xc = XML::LibXML::XPathContext->new();
$xc->registerNs('xsi', $uri);
my $schemaLocation = $xc->findnodes('@xsi:schemaLocation', $node)->pop();
if (defined($schemaLocation)) {
my @schemaList = split(/\s+/, $schemaLocation->nodeValue());
while (scalar(@schemaList) > 1) {
my $ns = shift(@schemaList);
my $file = shift(@schemaList);
addSchema($ns, $file);
}
if (scalar(@schemaList) == 1) {
report("schemaLocation tag has an odd number of fields",
$schemaLocation);
}
}
# Verify the node if it changes namespaces or is the root
my $parent = $node->parentNode();
my $parentns = $parent->namespaceURI();
if (!defined($parentns) || $parentns ne $nodens) {
verify($node);
}
# Recurse
for my $child ($node->childNodes()) {
if ($child->nodeType() == XML_ELEMENT_NODE) {
visit($child);
}
}
}
sub verify($)
{
my ($node) = @_;
my $ns = $node->namespaceURI();
if (! exists($ns2schema{$ns})) {
my $nodeStr = $node->nodeName();
print STDERR "Ignoring node $nodeStr with unknown namespace $ns\n";
return;
}
my $schema = $ns2schema{$ns};
eval {
my $clone = $node->cloneNode(1);
my $clonedoc = XML::LibXML::Document->createDocument("1.0", "UTF-8");
$clonedoc->setDocumentElement($clone);
$schema->validate($clonedoc);
};
if ($@) {
report("Failed validation with root at element: $@", $node);
}
}
sub report($$)
{
my ($error, $node) = @_;
my $line = $node->line_number();
my $nodeStr = $node->nodeName();
die("Line $line: $error: $nodeStr\n");
}
<rspec xmlns="http://www.protogeni.net/resources/rspec/2"
xmlns:delay="http://www.protogeni.net/resources/rspec/ext/delay/1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.protogeni.net/resources/rspec/2 http://\
www.protogeni.net/resources/rspec/2/request.xsd http://www.protogeni.net/resources/rspec/ext/delay/1 http://www.protogeni.net/resources/rspec/ext/delay/1/request-delay.xsd" type="request">
xsi:schemaLocation="http://www.protogeni.net/resources/rspec/2 http://www.protogeni.net/resources/rspec/2/request.xsd http://www.protogeni.net/resources/rspec/ext/delay/1 http://www.protogeni.net/resources/rspec/ext/delay/1/request.xsd" type="request">
<!-- This rspec implements the following topology:
left-link right-link
left <---------> bridge <---------> right
left <=========> bridge <==========> right
The "bridge" is a special node type (sliver_type="delay") that
tells the CM to insert an Emulab delay node instead of a plain
......@@ -35,14 +34,14 @@ www.protogeni.net/resources/rspec/2/request.xsd http://www.protogeni.net/resourc
exclusive="true">
<sliver_type name="raw-pc" />
<interface client_id="left:if0">
<ip address="10.10.10.1" mask="255.255.255.0" type="ipv4"/>
<ip address="10.10.10.1" netmask="255.255.255.0" type="ipv4"/>
</interface>
</node>
<node client_id="right"
exclusive="true">
<sliver_type name="raw-pc" />
<interface client_id="right:if0">
<ip address="10.10.10.2" mask="255.255.255.0" type="ipv4"/>
<ip address="10.10.10.2" netmask="255.255.255.0" type="ipv4"/>
</interface>
</node>
<node client_id="bridge"
......
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