#!/usr/bin/perl -w
#
# Copyright (c) 2003-2012 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
# This file is part of the Emulab network testbed software.
#
# This file is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
#
# This file is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
# License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this file. If not, see .
#
# }}}
#
use strict;
use English;
use Getopt::Std;
use Date::Parse;
use Time::Local;
use Data::Dumper;
use File::Temp qw(tempfile);
#
# Add a generic device to the DB.
#
sub usage()
{
print STDERR "Usage: addspecialdevice -t [-s count] \n";
print STDERR "Options:\n";
print STDERR " -d - Turn on debugging\n";
print STDERR " -n - Dry run mode\n";
print STDERR " -s count - Node is to be shared, provide capacity\n";
print STDERR " -t type - Type name (eg: bbg, spp, goober, whatever)\n";
exit(-1);
}
my $optlist = "t:dns:";
my $debug = 0;
my $impotent = 0;
my $type;
my $isshared = 0;
my $experiment;
# Protos
sub fatal($);
sub CreateType($$);
sub CreateNode();
#
# Configure variables
#
my $TB = "@prefix@";
#
# Testbed Support libraries
#
use lib "@prefix@/lib";
use emdb;
use EmulabConstants;
use emutil;
use User;
use Node;
use NodeType;
use OSinfo;
use Experiment;
use Interface;
#
# Turn off line buffering on output
#
$| = 1;
#
# Untaint the path
#
$ENV{'PATH'} = "/bin:/sbin:/usr/bin:";
#
# Parse command arguments.
#
my %options = ();
if (! getopts($optlist, \%options)) {
usage();
}
if (defined($options{'h'})) {
usage();
}
if (defined($options{'d'})) {
$debug = 1;
}
if (defined($options{'n'})) {
$impotent = 1;
}
if (defined($options{'t'})) {
$type = $options{'t'};
}
if (defined($options{'s'})) {
$isshared = $options{'s'};
}
usage()
if (@ARGV != 1 || !(defined($type)));
my $node_id = $ARGV[0];
#
# Verify user, must be admin.
#
my $this_user = User->ThisUser();
if (! defined($this_user)) {
fatal("You ($UID) do not exist!");
}
if (!$this_user->IsAdmin()) {
fatal("You are not a testbed administrator!");
}
#
# The node may not already exist.
#
if (Node->Lookup($node_id)) {
fatal("Node already exists in the DB");
}
#
# These two OSIDs must exist.
#
my $osinfo = OSinfo->LookupByName("GENERICDEV");
if (!defined($osinfo)) {
fatal("Cannot lookup OSID 'GENERICDEV' for physical device");
}
my $osinfo_vm = OSinfo->LookupByName("GENERICDEV-VM");
if (!defined($osinfo_vm)) {
fatal("Cannot lookup OSID 'GENERICDEV-VM' for virtual device");
}
#
# If the fakenode is shared, it has to be allocated to a holding
# experiment.
#
if ($isshared) {
$experiment = Experiment->Lookup(TBOPSPID(), "holding");
fatal("Holding experiment does not exist!")
if (!defined($experiment));
}
#
# Create the type if it does not exist.
#
my $nodetype = NodeType->Lookup($type);
if (!defined($nodetype)) {
CreateType($type, 0);
if ($isshared) {
CreateType("${type}-vm", 1);
}
$nodetype = NodeType->Lookup($type);
}
CreateNode();
exit(0);
#
# Add a new node type.
#
sub CreateType($$)
{
my ($type, $isvirt) = @_;
my $osid = ($isvirt ? $osinfo_vm->osid() : $osinfo->osid());
#
# Defaults work for almost everything.
#
if ($debug || $impotent) {
print "Creating type: $type - isvirt is $isvirt\n";
}
if (!$impotent) {
DBQueryFatal("replace into node_types set ".
" class='$type', type='$type', ".
" isvirtnode='$isvirt', isdynamic='$isvirt'");
if (0 && $isshared && $isvirt) {
DBQueryFatal("replace into node_auxtypes values ".
"('$node_id', '$type', '$isshared')");
}
}
#
# Device nodes are neither rebootable nor imageable.
#
if ($debug || $impotent) {
print "Adding false attributes rebootable/imageable\n";
print "Adding true attribute fakenode\n";
print "Setting default osid: " .
($isvirt ? $osinfo_vm->osname() : $osinfo->osname()) . "\n";
}
if (!$impotent) {
DBQueryFatal("replace into node_type_attributes values ".
" ('$type','rebootable','0','boolean')");
DBQueryFatal("replace into node_type_attributes values ".
" ('$type','imageable','0','boolean')");
DBQueryFatal("replace into node_type_attributes values ".
" ('$type','fakenode','1','boolean')");
DBQueryFatal("replace into node_type_attributes values ".
" ('$type','default_osid','$osid','integer')");
if ($isshared && !$isvirt) {
DBQueryFatal("replace into node_type_attributes values ".
" ('$type','virtnode_capacity', ".
" '$isshared','integer')");
}
}
return 0;
}
#
# Create the node.
#
sub CreateNode()
{
my $blob = {
"type" => $type,
"role" => ($nodetype->isswitch() ?
"testswitch" : "testnode"),
"initial_eventstate" => TBDB_NODESTATE_ISUP(),
};
my $node;
print "Creating new node $node_id\n";
if (!$impotent) {
$node = Node->Create($node_id, $experiment, $blob);
if (!defined($node)) {
fatal("Could not create new node");
}
}
#
# When the node is shared, need to update the sharing state.
#
if ($isshared) {
print "Updating reservation to reflect sharing.\n";
if (!$impotent) {
$node->ModifyReservation({"erole" => "sharedhost",
"sharing_mode" => "shared_local"})
== 0 or fatal("Could not update reservation entry");
}
}
return 0;
}
sub fatal($)
{
my ($mesg) = $_[0];
die("*** $0:\n".
" $mesg\n");
}