mirror of
https://github.com/xcat2/xcat-core.git
synced 2025-05-22 03:32:04 +00:00
904 lines
27 KiB
Perl
904 lines
27 KiB
Perl
# IBM(c) 2011 EPL license http://www.eclipse.org/legal/epl-v10.html
|
|
#-------------------------------------------------------
|
|
|
|
=head 1
|
|
|
|
xCAT plugin to handle xCAT UI service portal commands
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
|
|
package xCAT_plugin::webportal;
|
|
use strict;
|
|
require xCAT::Utils;
|
|
require xCAT::MsgUtils;
|
|
require xCAT::zvmUtils;
|
|
require xCAT::DBobjUtils;
|
|
require IO::Socket::INET;
|
|
use Getopt::Long;
|
|
use Data::Dumper;
|
|
use xCAT::Table;
|
|
use xCAT::NodeRange;
|
|
use XML::Simple;
|
|
require XML::Parser;
|
|
|
|
sub handled_commands {
|
|
|
|
# In order for this to work, you need to run: ln -s /opt/xcat/bin/xcatclientnnr /opt/xcat/bin/webportal
|
|
# xcatclientnnr allows command to run without a node range
|
|
return { webportal => "webportal" };
|
|
}
|
|
|
|
sub process_request {
|
|
my $request = shift;
|
|
my $callback = shift;
|
|
my $sub_req = shift;
|
|
my %authorized_cmds = (
|
|
'lszvm' => \&lszvm,
|
|
'provzlinux' => \&provzlinux,
|
|
'clonezlinux' => \&clonezlinux,
|
|
'genhostip' => \&genhostip,
|
|
'getmaxvm' => \&getmaxvm,
|
|
'getuserprivilege' => \&getuserprivilege,
|
|
'lsgoldenimages' => \&lsgoldenimages
|
|
);
|
|
|
|
# Check if the request is authorized
|
|
@_ = split ' ', $request->{arg}->[0];
|
|
my $cmd = $_[0];
|
|
if (grep { $_ eq $cmd } keys %authorized_cmds) {
|
|
my $func = $authorized_cmds{$cmd};
|
|
$func->($request, $callback, $sub_req);
|
|
}
|
|
else {
|
|
$callback->(
|
|
{ error => "$cmd is not authorized!\n", errorcode => [1] });
|
|
}
|
|
}
|
|
|
|
sub println {
|
|
my $callback = shift;
|
|
my $msg = shift;
|
|
my %rsp;
|
|
push @{ $rsp{info} }, $msg;
|
|
xCAT::MsgUtils->message('I', \%rsp, $callback);
|
|
return;
|
|
}
|
|
|
|
sub lszvm {
|
|
my ($request, $callback, $sub_req) = @_;
|
|
|
|
# List the zVM and their respective HCP
|
|
my $out = "";
|
|
my %pair;
|
|
|
|
# Look in 'zvm2' table for zVM host systems
|
|
my $tab2 = xCAT::Table->new('zvm', -create => 1, -autocommit => 0);
|
|
my @results2 = $tab2->getAllAttribsWhere("nodetype='zvm'", 'hcp', 'node');
|
|
foreach (@results2) {
|
|
if ($_->{'hcp'} && !$pair{ $_->{'hcp'} }) {
|
|
|
|
# Save zVM:HCP pairing
|
|
$pair{ $_->{'hcp'} } = $_->{'node'};
|
|
|
|
# Print out zVM:HCP
|
|
$out .= $_->{'node'} . ":" . $_->{'hcp'} . "\n";
|
|
}
|
|
}
|
|
|
|
# Look in 'zvm' table for zVM host systems that may not be setup with nodetype=zvm
|
|
# Look in 'zvm' table
|
|
my $tab = xCAT::Table->new('zvm', -create => 1, -autocommit => 0);
|
|
my @results = $tab->getAllAttribsWhere("nodetype='vm'", 'hcp', 'parent');
|
|
foreach (@results) {
|
|
if ($_->{'hcp'} && $_->{'parent'} && !$pair{ $_->{'hcp'} }) {
|
|
|
|
# Save zVM:HCP pairing
|
|
$pair{ $_->{'hcp'} } = $_->{'parent'};
|
|
|
|
# Print out zVM:HCP
|
|
$out .= $_->{'parent'} . ":" . $_->{'hcp'} . "\n";
|
|
}
|
|
}
|
|
|
|
$callback->({ data => $out });
|
|
}
|
|
|
|
sub provzlinux {
|
|
my ($request, $callback, $sub_req) = @_;
|
|
|
|
my $group = $request->{arg}->[1];
|
|
my $hcp = $request->{arg}->[2];
|
|
my $img = $request->{arg}->[3];
|
|
my $owner = $request->{arg}->[4];
|
|
|
|
# Exit if missing inputs
|
|
if (!$group || !$hcp || !$img || !$owner) {
|
|
println($callback, '(Error) Missing group, HCP, image, or owner');
|
|
return;
|
|
}
|
|
|
|
# Check the max # of virtual machines allowed
|
|
my $out = `/opt/xcat/sbin/tabdump nodetype -w nodetype.comments=~"owner:$owner"`;
|
|
my @tmp = split(/\n/, $out);
|
|
my $usrVM = scalar(@tmp) - 1;
|
|
|
|
$out = `/opt/xcat/bin/webportal getmaxvm $owner`;
|
|
$out =~ s/Max allowed: //g;
|
|
my $maxVM = int($out);
|
|
|
|
# Do not continue if the max # is reached
|
|
if ($usrVM >= $maxVM) {
|
|
println($callback, "You have reached the maximum number of virtual machines allowed ($maxVM). Delete unused virtual machines or contact your system administrator request more virtual machines.");
|
|
return;
|
|
}
|
|
|
|
# Get node OS base
|
|
my $profile;
|
|
my $arch;
|
|
my $os;
|
|
($profile, $arch, $os) = getosimagedef($callback, $img);
|
|
if ($os =~ m/sp/i) {
|
|
@tmp = split(/sp/, $os);
|
|
} else {
|
|
@tmp = split(/\./, $os);
|
|
}
|
|
my $os_base = $tmp[0];
|
|
|
|
# Read in default disk pool and disk size /opt/zhcp/conf/default.conf on zHCP
|
|
# pool = POOL3
|
|
# eckd_size = 10016
|
|
my $disk_pool;
|
|
my $eckd_size;
|
|
my $fba_size;
|
|
my $profile_conf = $profile;
|
|
my $default_conf = "/var/opt/xcat/profiles/$profile.conf";
|
|
my $default_direct = "/var/opt/xcat/profiles/$profile.direct";
|
|
|
|
# Check if a group based directory entry exists, else use default one
|
|
if (!(`test -e /var/opt/xcat/profiles/$profile.direct && echo Exists`)) {
|
|
println($callback, "$profile.direct does not exist. Using default.direct to generate directory entry.");
|
|
|
|
# Exit if default.direct does not exist
|
|
$default_direct = "/var/opt/xcat/profiles/default.direct";
|
|
$default_conf = "/var/opt/xcat/profiles/default.conf";
|
|
$profile_conf = "default";
|
|
if (!(`test -e $default_direct && echo Exists`)) {
|
|
println($callback, "(Error) $default_direct does not exists");
|
|
return;
|
|
}
|
|
}
|
|
|
|
# Exit if default.conf does not exist
|
|
if (!(`test -e $default_conf && echo Exists`)) {
|
|
println($callback, "(Error) $default_conf does not exists");
|
|
return;
|
|
}
|
|
|
|
# Exit if default.direct does not exist
|
|
if (!(`test -e $default_direct && echo Exists`)) {
|
|
println($callback, "(Error) $default_direct does not exists");
|
|
return;
|
|
}
|
|
|
|
$out = `cat $default_conf`;
|
|
@tmp = split(/\n/, $out);
|
|
|
|
# default.conf should contain:
|
|
|
|
# Configuration for virtual machines
|
|
# default_diskpool=POOL3
|
|
# default_eckd_size=10016
|
|
my $profile_diskpool_parm = $profile_conf . "_diskpool";
|
|
my $profile_eckd_size_parm = $profile_conf . "_eckd_size";
|
|
my $profile_fba_size_parm = $profile_conf . "_fba_size";
|
|
my $default_disk_pool;
|
|
my $default_eckd_size;
|
|
my $default_fba_size;
|
|
foreach (@tmp) {
|
|
|
|
# Get profile disk pool (default)
|
|
if ($_ =~ m/$profile_diskpool_parm=/i) {
|
|
$disk_pool = $_;
|
|
$disk_pool =~ s/$profile_diskpool_parm=//g;
|
|
}
|
|
|
|
# Get profile disk size (default)
|
|
elsif ($_ =~ m/$profile_eckd_size_parm=/i) {
|
|
$eckd_size = $_;
|
|
$eckd_size =~ s/$profile_eckd_size_parm=//g;
|
|
}
|
|
elsif ($_ =~ m/$profile_fba_size_parm=/i) {
|
|
$fba_size = $_;
|
|
$fba_size =~ s/$profile_fba_size_parm=//g;
|
|
}
|
|
}
|
|
|
|
# Use default configuration if profile configuration does not exist
|
|
if (!$disk_pool && (!$eckd_size || !$fba_size)) {
|
|
println($callback, "(Error) $profile_conf configuration for disk pool and size does not exist");
|
|
return;
|
|
}
|
|
|
|
my $site_tab = xCAT::Table->new('site');
|
|
my $hash = $site_tab->getAttribs({ key => "installdir" }, 'value');
|
|
my $install_dir = $hash->{'value'};
|
|
|
|
# Get autoyast/kickstart template
|
|
# Count the number of disks needed
|
|
my $tmpl;
|
|
if ($os =~ m/sles/i) {
|
|
$tmpl = "$install_dir/custom/install/sles/$profile.$os_base.$arch.tmpl";
|
|
} elsif ($os =~ m/rhel/i) {
|
|
$tmpl = "$install_dir/custom/install/rh/$profile.$os_base.$arch.tmpl";
|
|
}
|
|
|
|
# Create VM
|
|
# e.g. webportal provzlinux [group] [hcp] [image]
|
|
# my ($node, $ip, $base_digit) = gennodename( $callback, $group );
|
|
my ($node, $ip, $hostname) = findfreenode($callback, $group);
|
|
if (!$node) {
|
|
println($callback, "Unable to find a free node, IP, and hostname for $group from the IP pool");
|
|
return;
|
|
}
|
|
|
|
my $userid = $node;
|
|
|
|
# Set node definitions
|
|
# Also put node into all group
|
|
if ($group eq 'all') {
|
|
$out = `/opt/xcat/bin/mkdef -t node -o $node userid=$userid hcp=$hcp mgt=zvm groups=$group`;
|
|
} else {
|
|
|
|
# Put node in all group
|
|
$out = `/opt/xcat/bin/mkdef -t node -o $node userid=$userid hcp=$hcp mgt=zvm groups=$group`;
|
|
}
|
|
println($callback, "$out");
|
|
|
|
# Set nodetype definitions
|
|
$out = `/opt/xcat/sbin/chtab node=$node hosts.ip=$ip hosts.hostnames=$hostname noderes.netboot=zvm nodetype.nodetype=osi nodetype.provmethod=install nodetype.os=$os nodetype.arch=$arch nodetype.profile=$profile nodetype.comments="owner:$owner"`;
|
|
|
|
# Create user directory entry replacing LXUSR with user ID
|
|
# Use /opt/zhcp/conf/default.direct on zHCP as the template
|
|
# USER LXUSR PSWD 512M 1G G
|
|
# INCLUDE LNXDFLT
|
|
# COMMAND SET VSWITCH VSW2 GRANT LXUSR
|
|
$out = `sed $default_direct -e s/LXUSR/$userid/g > /tmp/$node-direct.txt`;
|
|
$out = `/opt/xcat/bin/mkvm $node /tmp/$node-direct.txt`;
|
|
`rm -rf /tmp/$node-direct.txt`;
|
|
println($callback, "$out");
|
|
if ($out =~ m/Error/i) {
|
|
return;
|
|
}
|
|
|
|
# Add MDISKs to user directory entry
|
|
# Use /opt/zhcp/conf/default.conf on zHCP to determine disk pool and disk size
|
|
# pool = POOL3
|
|
# eckd_size = 10016
|
|
|
|
my $type;
|
|
my $virt_addr;
|
|
if ($os =~ m/sles/i) {
|
|
|
|
# Create XML object
|
|
my $xml = new XML::Simple;
|
|
|
|
# Read XML file
|
|
my $data = $xml->XMLin($tmpl);
|
|
|
|
my $devices_ref = $data->{'dasd'}->{'devices'}->{'listentry'};
|
|
my @devices;
|
|
|
|
if (ref($devices_ref) eq 'HASH') {
|
|
|
|
# In the case of 1 device in the listentry, push hash into array
|
|
push(@devices, $devices_ref);
|
|
} else {
|
|
|
|
# Listentry is an array reference
|
|
@devices = @$devices_ref;
|
|
}
|
|
|
|
foreach (@devices) {
|
|
|
|
# Get disk virtual address and disk type
|
|
$type = $_->{'drivers'}->{'listentry'}->{'modules'}->{'module_entry'}->{'listentry'};
|
|
$virt_addr = $_->{'sysfs_bus_id'};
|
|
$virt_addr =~ s/0\.0\.//g;
|
|
foreach (@$type) {
|
|
|
|
# Add ECKD disk
|
|
if ($_ =~ m/dasd_eckd_mod/i) {
|
|
$out = `/opt/xcat/bin/chvm $node --add3390 $disk_pool $virt_addr $eckd_size MR`;
|
|
println($callback, "$out");
|
|
if ($out =~ m/Error/i) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
# Add FBA disk
|
|
elsif ($_ =~ m/dasd_fba_mod/i) {
|
|
|
|
# To be continued
|
|
# $out = `chvm $node --add9336 $disk_pool $virt_addr $fba_size MR`;
|
|
}
|
|
}
|
|
} # End of foreach
|
|
} elsif ($os =~ m/rhel/i) {
|
|
my %devices;
|
|
my $dev;
|
|
$virt_addr = 100;
|
|
|
|
# Read in kickstart file
|
|
$out = `cat $tmpl | egrep "part /"`;
|
|
@tmp = split(/\n/, $out);
|
|
foreach (@tmp) {
|
|
$out = substr($out, index($out, '--ondisk=') + 9);
|
|
$out =~ s/\s*$//; # Trim right
|
|
$out =~ s/^\s*//; # Trim left
|
|
$devices{$out} = 1;
|
|
}
|
|
|
|
# Add ECKD disk for each device found
|
|
for $dev (keys %devices) {
|
|
$out = `/opt/xcat/bin/chvm $node --add3390 $disk_pool $virt_addr $eckd_size MR`;
|
|
println($callback, "$out");
|
|
if ($out =~ m/Error/i) {
|
|
return;
|
|
}
|
|
|
|
# Increment virtual address
|
|
$virt_addr = $virt_addr + 1;
|
|
}
|
|
}
|
|
|
|
# Update hosts table
|
|
`/opt/xcat/sbin/makehosts`;
|
|
|
|
# Toggle node power so COMMAND SET will get executed
|
|
`/opt/xcat/bin/rpower $node on`;
|
|
`/opt/xcat/bin/rpower $node off`;
|
|
|
|
# Punch kernel, initrd, and ramdisk to node reader
|
|
$out = `/opt/xcat/sbin/nodeset $node install`;
|
|
println($callback, "$out");
|
|
if ($out =~ m/Error/i) {
|
|
return;
|
|
}
|
|
|
|
# IPL reader and begin installation
|
|
$out = `/opt/xcat/bin/rnetboot $node ipl=00C`;
|
|
println($callback, "$out");
|
|
if ($out =~ m/Error/i) {
|
|
return;
|
|
}
|
|
|
|
# Configure Ganglia monitoring
|
|
$out = `/opt/xcat/bin/moncfg gangliamon $node -r`;
|
|
|
|
# Show node information, e.g. IP, hostname, and root password
|
|
$out = `/opt/xcat/bin/lsdef $node -i ip,hostnames | egrep "ip=|hostnames="`;
|
|
my $rootpw = getsysrootpw();
|
|
println($callback, "Your virtual machine is ready. It may take a few minutes before you can logon using VNC ($node:1). Below is your VM attributes.");
|
|
println($callback, "$out");
|
|
println($callback, " rootpw = $rootpw");
|
|
}
|
|
|
|
sub getsysrootpw {
|
|
|
|
# Get the default root password for all xCAT provisioned VM
|
|
my ($callback) = @_;
|
|
|
|
my $tab = xCAT::Table->new('passwd');
|
|
my $hash = $tab->getAttribs({ key => "system" }, 'password');
|
|
my $passwd = $hash->{'password'};
|
|
|
|
return $passwd;
|
|
}
|
|
|
|
sub getosimagedef {
|
|
|
|
# Get osimage definitions based on image name
|
|
my ($callback, $img_name) = @_;
|
|
|
|
my $profile;
|
|
my $arch;
|
|
my $os;
|
|
|
|
# Get profile, osarch, and osver in 'osimage' table based on imagename
|
|
my $tab = xCAT::Table->new('osimage', -create => 1, -autocommit => 0);
|
|
my @results = $tab->getAllAttribsWhere("imagename='" . $img_name . "'",
|
|
'profile', 'osarch', 'osvers');
|
|
foreach (@results) {
|
|
|
|
# It should return: |gpok(\d+)|10.1.100.($1+0)|
|
|
$profile = $_->{'profile'};
|
|
$arch = $_->{'osarch'};
|
|
$os = $_->{'osvers'};
|
|
}
|
|
|
|
return ($profile, $arch, $os);
|
|
}
|
|
|
|
sub gennodename {
|
|
|
|
# Generate node name based on given group
|
|
my ($callback, $group) = @_;
|
|
|
|
# Only use the 1st group
|
|
if ($group =~ m/,/) {
|
|
my @groups = split(',', $group);
|
|
$group = @groups[0];
|
|
}
|
|
|
|
# Hostname and IP address regular expressions
|
|
my $hostname_regex;
|
|
my $ipaddr_regex;
|
|
|
|
my @comments;
|
|
my $base_digit = 0;
|
|
my $base_hostname;
|
|
my $base_ipaddr;
|
|
|
|
# Network, submask, submask prefix, and host ranges
|
|
my $network = "";
|
|
my $mask;
|
|
my $prefix;
|
|
my $hosts_count;
|
|
my $range_low = 1;
|
|
my $range_high = 254;
|
|
|
|
# Hostname and IP address generated
|
|
my $hostname;
|
|
my $ipaddr;
|
|
my $tmp;
|
|
|
|
my @args;
|
|
|
|
# Get regular expression for hostname in 'hosts' table
|
|
my $tab = xCAT::Table->new('hosts', -create => 1, -autocommit => 0);
|
|
my @results = $tab->getAllAttribsWhere("node='" . $group . "'", 'ip', 'comments');
|
|
foreach (@results) {
|
|
|
|
# It should return: |gpok(\d+)|10.1.100.($1+0)|
|
|
@args = split(/\|/, $_->{'ip'});
|
|
$hostname_regex = $args[1];
|
|
$ipaddr_regex = $args[2];
|
|
|
|
$base_hostname = $args[1];
|
|
$base_hostname =~ s/\(\S*\)/#/g;
|
|
|
|
# Get the 10.1.100.
|
|
$base_ipaddr = $args[2];
|
|
$base_ipaddr =~ s/\(\S*\)//g;
|
|
|
|
# Get the ($1+0)
|
|
$ipaddr_regex =~ s/$base_ipaddr//g;
|
|
|
|
# Get the network within comments
|
|
# It should return: "description: All machines; network: 10.1.100.0/24;"
|
|
# This will help determine the 1st node in the group if none exists
|
|
@comments = split(/|/, $_->{'comments'});
|
|
foreach (@comments) {
|
|
if ($_ =~ m/network:/i) {
|
|
$network = $_;
|
|
|
|
# Remove network header
|
|
$network =~ s/network://g;
|
|
|
|
# Trim network section
|
|
$network =~ s/\s*$//;
|
|
$network =~ s/^\s*//;
|
|
|
|
# Extract network
|
|
$tmp = rindex($network, '/');
|
|
if ($tmp > -1) {
|
|
|
|
# Get submask prefix
|
|
$prefix = substr($network, $tmp);
|
|
$prefix =~ s|/||g;
|
|
|
|
# Get the number of hosts possible using submask
|
|
$hosts_count = 32 - int($prefix);
|
|
|
|
# Minus network and broadcast addresses
|
|
$hosts_count = 2**$hosts_count - 2;
|
|
|
|
# Get network
|
|
$network = substr($network, 0, $tmp);
|
|
}
|
|
|
|
# Extract base digit, which depends on the netmask used
|
|
$base_digit = substr($network, rindex($network, '.') + 1);
|
|
|
|
# 1st number in range is network
|
|
$range_low = $base_digit + 1;
|
|
|
|
# Get hosts range
|
|
if ($tmp > -1) {
|
|
$range_high = $base_digit + $hosts_count;
|
|
}
|
|
}
|
|
} # End of foreach
|
|
} # End of foreach
|
|
|
|
# Generate hostname
|
|
$hostname = $base_hostname;
|
|
$hostname =~ s/#/$base_digit/g;
|
|
|
|
# Generate IP address
|
|
$ipaddr = $hostname;
|
|
$ipaddr =~ s/$hostname_regex/$ipaddr_regex/gee;
|
|
$ipaddr = $base_ipaddr . $ipaddr;
|
|
|
|
# Get networks in 'networks' table
|
|
$tab = xCAT::Table->new('networks', -create => 1, -autocommit => 0);
|
|
my $entries = $tab->getAllEntries();
|
|
|
|
# Go through each network
|
|
my $iprange;
|
|
foreach (@$entries) {
|
|
|
|
# Get network, mask, and range
|
|
$network = $_->{'net'};
|
|
$mask = $_->{'mask'};
|
|
$iprange = $_->{'dynamicrange'};
|
|
|
|
# If the host IP address is in this subnet, return
|
|
if (xCAT::NetworkUtils->ishostinsubnet($ipaddr, $mask, $network)) {
|
|
|
|
# Exit loop
|
|
last;
|
|
} else {
|
|
$network = "";
|
|
}
|
|
}
|
|
|
|
# Exit if no network exist for group
|
|
if (!$network) {
|
|
return;
|
|
}
|
|
|
|
# Find the network range for this group based on networks table
|
|
my @ranges;
|
|
if ($iprange) {
|
|
@args = split(/;/, $iprange);
|
|
foreach (@args) {
|
|
|
|
# If a network range exists
|
|
if ($_ =~ m/-/) {
|
|
@ranges = split(/-/, $_);
|
|
$range_low = $ranges[0];
|
|
$range_high = $ranges[1];
|
|
|
|
# Get the low and high ends digit
|
|
$range_low =~ s/$base_ipaddr//g;
|
|
$range_high =~ s/$base_ipaddr//g;
|
|
}
|
|
}
|
|
} # End of if ($iprange)
|
|
|
|
# If no nodes exist in group
|
|
# Set the base digit to the low end of the network range
|
|
if ($range_low && $base_digit == 1) {
|
|
$base_digit = $range_low;
|
|
|
|
# Generate hostname
|
|
$hostname = $base_hostname;
|
|
$hostname =~ s/#/$base_digit/g;
|
|
|
|
# Generate IP address
|
|
$ipaddr = $hostname;
|
|
$ipaddr =~ s/$hostname_regex/$ipaddr_regex/gee;
|
|
$ipaddr = $base_ipaddr . $ipaddr;
|
|
}
|
|
|
|
# Check xCAT tables, /etc/hosts, and ping to see if hostname is already used
|
|
while (`/opt/xcat/bin/nodels $hostname` || `cat /etc/hosts | grep "$ipaddr "` || !(`ping -c 4 $ipaddr` =~ m/100% packet loss/)) {
|
|
|
|
# Base digit invalid if over 254
|
|
if ($base_digit > $range_high) {
|
|
last;
|
|
}
|
|
|
|
# +1 to base digit to obtain next hostname
|
|
$base_digit = $base_digit + 1;
|
|
|
|
$hostname = $base_hostname;
|
|
$hostname =~ s/#/$base_digit/g;
|
|
|
|
$ipaddr = $hostname;
|
|
$ipaddr =~ s/$hostname_regex/$ipaddr_regex/gee;
|
|
$ipaddr = $base_ipaddr . $ipaddr;
|
|
}
|
|
|
|
# Range must be within network range
|
|
if ($base_digit > $range_high) {
|
|
return;
|
|
} else {
|
|
return ($hostname, $ipaddr, $base_digit);
|
|
}
|
|
}
|
|
|
|
sub clonezlinux {
|
|
my ($request, $callback, $sub_req) = @_;
|
|
|
|
# webportal clonezlinux [src node] [group] [owner]
|
|
my $src_node = $request->{arg}->[1];
|
|
my $group = $request->{arg}->[2];
|
|
my $owner = $request->{arg}->[3];
|
|
|
|
# Check the max # of virtual machines allowed
|
|
my $out = `/opt/xcat/sbin/tabdump nodetype -w nodetype.comments=~"owner:$owner"`;
|
|
my @tmp = split(/\n/, $out);
|
|
my $usrVM = scalar(@tmp) - 1;
|
|
|
|
$out = `/opt/xcat/bin/webportal getmaxvm $owner`;
|
|
$out =~ s/Max allowed: //g;
|
|
my $maxVM = int($out);
|
|
|
|
# Do not continue if the max # is reached
|
|
if ($usrVM >= $maxVM) {
|
|
println($callback, "You have reached the maximum number of virtual machines allowed ($maxVM). Delete unused virtual machines or contact your system administrator request more virtual machines.");
|
|
return;
|
|
}
|
|
|
|
# Get source node's HCP
|
|
my $props = xCAT::zvmUtils->getNodeProps('zvm', $src_node, ('hcp'));
|
|
my $hcp = $props->{'hcp'};
|
|
|
|
# Get source node's nodetype
|
|
$props = xCAT::zvmUtils->getNodeProps('nodetype', $src_node, ('os', 'arch', 'profile'));
|
|
my $os = $props->{'os'};
|
|
my $arch = $props->{'arch'};
|
|
my $profile = $props->{'profile'};
|
|
|
|
# Read in default disk pool from /var/opt/xcat/profiles/default.conf on xCAT MN
|
|
# pool = POOL3
|
|
# eckd_size = 10016
|
|
my $disk_pool;
|
|
my $default_conf = '/var/opt/xcat/profiles/default.conf';
|
|
my $default_direct = '/var/opt/xcat/profiles/default.direct';
|
|
|
|
# Exit if default.conf does not exist
|
|
if (!(`test -e $default_conf && echo Exists`)) {
|
|
println($callback, "(Error) $default_conf does not exists");
|
|
return;
|
|
}
|
|
|
|
# Exit if default.direct does not exist
|
|
if (!(`test -e $default_direct && echo Exists`)) {
|
|
println($callback, "(Error) $default_direct does not exists");
|
|
return;
|
|
}
|
|
|
|
$out = `cat $default_conf`;
|
|
@tmp = split(/\n/, $out);
|
|
|
|
# default.conf should contain:
|
|
|
|
# Default configuration for virtual machines handled by this zHCP
|
|
# default_diskpool=POOL3
|
|
# compute_diskpool=POOL3
|
|
my $profile_diskpool_parm = '';
|
|
if ($profile) {
|
|
$profile_diskpool_parm = $profile . "_diskpool";
|
|
}
|
|
|
|
foreach (@tmp) {
|
|
|
|
# Get disk pool (default)
|
|
if ($_ =~ m/default_diskpool=/i) {
|
|
$disk_pool = $_;
|
|
$disk_pool =~ s/default_diskpool=//g;
|
|
}
|
|
|
|
# Get profile disk pool (default)
|
|
elsif ($_ =~ m/$profile_diskpool_parm=/i && $profile_diskpool_parm) {
|
|
$disk_pool = $_;
|
|
$disk_pool =~ s/$profile_diskpool_parm=//g;
|
|
}
|
|
}
|
|
|
|
# Trim disk pool of white space
|
|
$disk_pool =~ s/\s*$//; # Trim right
|
|
$disk_pool =~ s/^\s*//; # Trim left
|
|
|
|
# Create VM
|
|
# e.g. webportal provzlinux [group] [hcp] [image]
|
|
# my ($node, $ip, $base_digit) = gennodename( $callback, $group );
|
|
my ($node, $ip, $hostname) = findfreenode($callback, $group);
|
|
if (!$node) {
|
|
println($callback, "Unable to find a free node, IP, and hostname for $group from the IP pool");
|
|
return;
|
|
}
|
|
|
|
my $userid = $node;
|
|
|
|
# Set node definitions
|
|
$out = `/opt/xcat/bin/mkdef -t node -o $node userid=$userid hcp=$hcp mgt=zvm groups=$group`;
|
|
println($callback, "$out");
|
|
|
|
# Set nodetype definitions
|
|
$out = `/opt/xcat/sbin/chtab node=$node hosts.ip=$ip hosts.hostnames=$hostname noderes.netboot=zvm nodetype.nodetype=osi nodetype.provmethod=install nodetype.os=$os nodetype.arch=$arch nodetype.profile=$profile nodetype.comments="owner:$owner"`;
|
|
|
|
# Update hosts table
|
|
sleep(5); # Time needed to update /etc/hosts
|
|
`/opt/xcat/sbin/makehosts`;
|
|
println($callback, "hosts table updated");
|
|
|
|
# Clone virtual machine
|
|
sleep(5); # Time needed to update /etc/hosts
|
|
$out = `/opt/xcat/bin/mkvm $node $src_node pool=$disk_pool`;
|
|
println($callback, "$out");
|
|
if ($out =~ m/Error/i || $out =~ m/Failed/i) {
|
|
return;
|
|
}
|
|
|
|
# Show node information, e.g. IP, hostname, and root password
|
|
$out = `/opt/xcat/bin/lsdef $node | egrep "ip=|hostnames="`;
|
|
my $rootpw = getsysrootpw();
|
|
println($callback, "Your virtual machine is ready. It may take a few minutes before you can logon. Below is your VM attributes.");
|
|
println($callback, "$out");
|
|
println($callback, " rootpw = Same as source node");
|
|
}
|
|
|
|
sub genhostip {
|
|
my ($request, $callback, $sub_req) = @_;
|
|
my $group = $request->{arg}->[1];
|
|
|
|
# my ($node, $ip, $base_digit) = gennodename( $callback, $group );
|
|
my ($node, $ip, $hostname) = findfreenode($callback, $group);
|
|
println($callback, "$node: $ip, $hostname");
|
|
}
|
|
|
|
sub getmaxvm {
|
|
my ($request, $callback, $sub_req) = @_;
|
|
my $user = $request->{arg}->[1];
|
|
|
|
my @args;
|
|
my $max;
|
|
|
|
# Look in 'policy' table
|
|
my $tab = xCAT::Table->new('policy', -create => 1, -autocommit => 0);
|
|
my @results = $tab->getAllAttribsWhere("name='" . $user . "'", 'comments');
|
|
foreach (@results) {
|
|
if ($_->{'comments'}) {
|
|
@args = split(';', $_->{'comments'});
|
|
|
|
# Extract max VM
|
|
foreach (@args) {
|
|
if ($_ =~ m/max-vm:/i) {
|
|
$_ =~ s/max-vm://g;
|
|
$max = $_;
|
|
last;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$callback->({ data => "Max allowed: $max" });
|
|
}
|
|
|
|
sub getuserprivilege {
|
|
|
|
# Get the user privilege
|
|
my ($request, $callback, $sub_req) = @_;
|
|
my $user = $request->{arg}->[1];
|
|
if (!$user) {
|
|
$callback->({ data => "(Error) No user name is specified" });
|
|
return;
|
|
}
|
|
|
|
my @args;
|
|
my $privilege = "user";
|
|
|
|
# Look in 'policy' table
|
|
my $tab = xCAT::Table->new('policy', -create => 1, -autocommit => 0);
|
|
my @results = $tab->getAllAttribsWhere("name='" . $user . "'", 'comments');
|
|
foreach (@results) {
|
|
if ($_->{'comments'}) {
|
|
@args = split(';', $_->{'comments'});
|
|
|
|
# Extract user privilege
|
|
foreach (@args) {
|
|
if ($_ =~ m/privilege:/i) {
|
|
$_ =~ s/privilege://g;
|
|
$privilege = $_;
|
|
$privilege =~ s/\s*$//; # Trim right
|
|
$privilege =~ s/^\s*//; # Trim left
|
|
last;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$callback->({ data => "Privilege: $privilege" });
|
|
}
|
|
|
|
sub lsgoldenimages {
|
|
my ($request, $callback, $sub_req) = @_;
|
|
|
|
# Find the golden image that can be cloned by searching nodetype table for nodetype.provmethod=clone
|
|
my $clones = "";
|
|
my $comments = "";
|
|
my $description = "";
|
|
my @args;
|
|
|
|
# Look in 'policy' table
|
|
my $tab = xCAT::Table->new('nodetype', -create => 1, -autocommit => 0);
|
|
my @results = $tab->getAllAttribsWhere("provmethod='clone'", 'node', 'comments');
|
|
foreach (@results) {
|
|
if ($_->{'node'}) {
|
|
$clones .= $_->{'node'} . ": ";
|
|
|
|
$comments = $_->{'comments'};
|
|
@args = split(';', $comments);
|
|
foreach (@args) {
|
|
if ($_ =~ m/description:/i) {
|
|
$description = $_;
|
|
$description =~ s/description://g;
|
|
$description =~ s/\s*$//; # Trim right
|
|
$description =~ s/^\s*//; # Trim left
|
|
} else {
|
|
$description = "No comments";
|
|
}
|
|
}
|
|
|
|
$clones .= $description . ",";
|
|
}
|
|
}
|
|
|
|
# Delete last comma
|
|
$clones = substr($clones, 0, -1);
|
|
|
|
$callback->({ data => $clones });
|
|
}
|
|
|
|
sub findfreenode {
|
|
|
|
# Generate node name based on given group
|
|
my ($callback, $group) = @_;
|
|
|
|
# IP pool contained in /var/opt/xcat/ippool where a file exists per group
|
|
if (!(`test -e /var/opt/xcat/ippool/$group.pool && echo Exists`)) {
|
|
return;
|
|
}
|
|
|
|
# IP pool group format: node, IP, hostname
|
|
# It would look similar to:
|
|
# ihost10,10.1.136.10,ihost10.endicott.ibm.com
|
|
# ihost11,10.1.136.11,ihost11.endicott.ibm.com
|
|
# ihost12,10.1.136.12,ihost12.endicott.ibm.com
|
|
my $node;
|
|
my $ipaddr;
|
|
my $hostname;
|
|
|
|
my $out = `cat /var/opt/xcat/ippool/$group.pool | grep -v "#"`;
|
|
my @entries = split(/\n/, $out);
|
|
if (@entries < 1) {
|
|
return;
|
|
}
|
|
|
|
my $found = 0;
|
|
foreach (@entries) {
|
|
|
|
# Grab the 1st free entry found
|
|
($node, $ipaddr, $hostname) = split(/,/, $_);
|
|
if ($node && $ipaddr && $hostname) {
|
|
|
|
# Check against xCAT tables, /etc/hosts, and ping to see if hostname is already used
|
|
if (`/opt/xcat/bin/nodels $node` || `cat /etc/hosts | grep "$ipaddr "` || !(`ping -c 4 $ipaddr` =~ m/100% packet loss/)) {
|
|
next;
|
|
} else {
|
|
$found = 1;
|
|
return ($node, $ipaddr, $hostname);
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
1;
|