#!/usr/bin/perl -w # # Copyright (c) 2003-2014 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 management interface for a node. ilo, ilo2, ilo3, drac. # When adding for a local node, try to insert switch info if -s is given. # Also restart DHCPD since we now add entries for local management interfaces. # sub usage() { print STDERR "Usage: management_iface -t -a [key|pswd] "; print STDERR " [-s ] mac IP arg1 arg2\n"; print STDERR " management_iface -r \n"; print STDERR " -h This message\n"; print STDERR " -t type Management type; ilo, ilo2, ilo3, drac, ipmi15, ipmi20\n"; print STDERR " -s info Optional switch info; switch,card,port\n"; print STDERR " -s - Search output of switchmac to find switch info\n"; print STDERR " -a pswd Password auth; provide login and password.\n"; print STDERR " -a key SSH key auth; provide login and key path.\n"; print STDERR " -r Remove management interface from DB.\n"; exit(-1); } my $optlist = "ht:a:rs:"; my $debug = 0; my $remove = 0; my $authtype; my $type; my $switchinfo; # Protos sub fatal($); sub RemoveManagementInterface($); # # Configure variables # my $TB = "@prefix@"; my $SWITCHMAC = "$TB/libexec/switchmac"; my $DHCPD_MAKECONF = "$TB/sbin/dhcpd_makeconf"; my $NAMED_SETUP = "$TB/sbin/named_setup"; # # Testbed Support libraries # use lib "@prefix@/lib"; use emdb; use EmulabConstants; use emutil; use User; use Node; use Interface; # # Turn off line buffering on output # $| = 1; # # Untaint the path # $ENV{'PATH'} = "/bin:/sbin:/usr/bin:"; # # Parse command arguments. Once we return from getopts, all that should be # left are the required arguments. # my %options = (); if (! getopts($optlist, \%options)) { usage(); } if (defined($options{'h'})) { usage(); } if (defined($options{'d'})) { $debug = 1; } if (defined($options{'r'})) { $remove = 1; usage() if (@ARGV != 1); } if (defined($options{'s'})) { $switchinfo = $options{'s'}; } if (defined($options{'t'})) { $type = $options{'t'}; usage() if ($type !~ /^(ilo|ilo2|ilo3|drac|ipmi15|ipmi20)$/); } if (defined($options{'a'})) { $authtype = $options{'a'}; usage() if (! ($authtype eq "key" || $authtype eq "pswd")); } # Need at least one argument. usage() if (@ARGV < 1); # # 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!"); } my $node_id = shift(); my $node = Node->Lookup($node_id); if (!defined($node)) { fatal("No such node!"); } # # Remove existing interface. # if ($remove) { exit(RemoveManagementInterface($node)); } # # Else adding new one. # usage() if (@ARGV != 4); my $mac = shift(); my $IP = shift(); # Convert mac to DB representation. if ($mac =~ /(\w\w):(\w\w):(\w\w):(\w\w):(\w\w):(\w\w)/) { $mac = lc("$1$2$3$4$5$6"); } # Verify optional switch info my $switchid; my $switchcard; my $switchport; if (defined($switchinfo)) { if ($switchinfo eq "-") { # # Search using switchmac # my $output = emutil::ExecQuiet($SWITCHMAC); if ($?) { fatal("Error running $SWITCHMAC:\n$output"); } foreach my $line (split("\n", $output)) { my ($smac,$sinfo,undef,undef,$role) = split(',', $line); if ($smac eq $mac) { print "Found mac on switch: $line\n"; if ($sinfo =~ /([-\w]*)\/(\d*)\.(\d*)/) { $switchid = $1; $switchcard = $2; $switchport = $3; } else { fatal("Could not parse switchmac data"); } last; } } if (!defined($switchid)) { print STDERR "Could not find the switch info with switchmac.\n"; print STDERR "Ping the current IP address and then try again\n"; exit(1); } } elsif ($switchinfo =~ /^([-\w]+),(\d+),(\d+)$/) { $switchid = $1; $switchcard = $2; $switchport = $3; } else { fatal("Invalid switch info"); } } my $isipmi = ($type =~ /^ipmi/ ? 1 : 0); # # Add the outlet and authinfo. # $node->AddOutlet($type, 0, {"key_type" => $type, "key_role" => ($authtype eq "key" ? ($isipmi ? "ipmi-kgkey" : "ssh-key") : ($isipmi ? "ipmi-passwd" : "ssh-passwd")), "key_uid" => $ARGV[0], "key" => $ARGV[1]}) == 0 or fatal("Could not add outlet records"); # # Add the interface, if not there. # my $interface = Interface->LookupManagement($node); if (defined($interface)) { # # Check to make sure consistent. # fatal("Inconsistent mac in $interface") if ($interface->mac() ne $mac); fatal("Inconsistent ip in $interface") if ($interface->IP() ne $IP); if (defined($switchinfo)) { fatal("Inconsistent switch_id in $interface") if ($interface->switch_id() ne $switchid); fatal("Inconsistent switch_port in $interface") if ($interface->switch_port() != $switchport); fatal("Inconsistent switch_card in $interface") if ($interface->switch_card() ne $switchcard); } } else { my $ifaceargs = { "card" => 99, # XXX Bogus. "iface" => $type, "role" => TBDB_IFACEROLE_MANAGEMENT(), "MAC" => $mac, "IP" => $IP, "type" => $type, }; if (defined($switchinfo)) { $ifaceargs->{'switch_id'} = $switchid; $ifaceargs->{'switch_port'} = $switchport; $ifaceargs->{'switch_card'} = $switchcard; } Interface->Create($node, $ifaceargs) or fatal("Could not create interface entry"); } # # Restart DHCPD. # if (!$node->isremotenode()) { print "Re-generating dhcpd.conf and restarting dhcpd\n"; if (system("$DHCPD_MAKECONF -i -r")) { fatal("Unable to restart dhcpd"); } print "Re-generating named config and restarting named\n"; if (system("$NAMED_SETUP")) { fatal("Unable to restart named"); } } exit(0); # # Remove a management interface. # sub RemoveManagementInterface($) { my ($node) = @_; my $interface = Interface->LookupManagement($node); fatal("No management interface") if (!defined($interface)); $node->DeleteOutlet() == 0 or fatal("Could not delete outlets"); $interface->DeleteWire() == 0 or fatal("Could not delete wire for $interface"); # Flag indicates it is okay to delete real interface. $interface->Delete(1) == 0 or fatal("Could not delete $interface"); # # Restart DHCPD. # if (!$node->isremotenode()) { print "Re-generating dhcpd.conf and restarting dhcpd\n"; if (system("$DHCPD_MAKECONF -i -r")) { fatal("Unable to restart dhcpd"); } return 0; } } sub fatal($) { my ($mesg) = $_[0]; die("*** $0:\n". " $mesg\n"); }