mirror of
				https://github.com/xcat2/xcat-core.git
				synced 2025-11-03 21:02:34 +00:00 
			
		
		
		
	git-svn-id: https://svn.code.sf.net/p/xcat/code/xcat-core/trunk@11642 8638fb3e-16cb-4fca-ae20-7b5d299a9bcd
		
			
				
	
	
		
			772 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
			
		
		
	
	
			772 lines
		
	
	
		
			21 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::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
 | 
						|
	);
 | 
						|
 | 
						|
	# 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 '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 = `tabdump nodetype -w nodetype.comments=~"owner:$owner"`;
 | 
						|
	my @tmp = split( /\n/, $out );
 | 
						|
	my $usrVM = scalar(@tmp) - 1;
 | 
						|
	
 | 
						|
	$out = `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 $default_conf   = '/opt/zhcp/conf/default.conf';
 | 
						|
	my $default_direct = "/opt/zhcp/conf/profiles/$profile.direct";
 | 
						|
 | 
						|
	# Check if a group based directory entry exists, else use default one
 | 
						|
	if ( !(`ssh $hcp "test -e /opt/zhcp/conf/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 = '/opt/zhcp/conf/profiles/default.direct';	
 | 
						|
		if ( !(`ssh $hcp "test -e /opt/zhcp/conf/profiles/default.direct && echo Exists"`) ) {
 | 
						|
			println( $callback, '(Error) $default_direct does not exists' );
 | 
						|
			return;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	# Exit if default.conf does not exist
 | 
						|
	if ( !(`ssh $hcp "test -e $default_conf && echo Exists"`) ) {
 | 
						|
		println( $callback, '(Error) $default_conf does not exists' );
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	# Exit if default.direct does not exist
 | 
						|
	if ( !(`ssh $hcp "test -e $default_direct && echo Exists"`) ) {
 | 
						|
		println( $callback, '(Error) $default_direct does not exists' );
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	$out = `ssh $hcp "cat $default_conf"`;
 | 
						|
	@tmp = split( /\n/, $out );
 | 
						|
	# default.conf should contain:
 | 
						|
	
 | 
						|
	# Default configuration for virtual machines handled by this zHCP
 | 
						|
	#	default_diskpool=POOL3
 | 
						|
	#	default_eckd_size=10016
 | 
						|
	#	compute_diskpool=POOL3
 | 
						|
	#	compute_eckd_size=10016	
 | 
						|
	my $profile_diskpool_parm = $profile . "_diskpool";
 | 
						|
	my $profile_eckd_size_parm = $profile . "_eckd_size";
 | 
						|
	my $profile_fba_size_parm = $profile . "_fba_size";
 | 
						|
	my $default_disk_pool;
 | 
						|
	my $default_eckd_size;
 | 
						|
	my $default_fba_size;
 | 
						|
	foreach (@tmp) {
 | 
						|
		# Get disk pool (default)
 | 
						|
		if ( $_ =~ m/default_diskpool=/i ) {
 | 
						|
			$default_disk_pool = $_;
 | 
						|
			$default_disk_pool =~ s/default_diskpool=//g;
 | 
						|
		}		
 | 
						|
		# Get disk size (default)
 | 
						|
		elsif ( $_ =~ m/default_eckd_size=/i ) {
 | 
						|
			$default_eckd_size = $_;
 | 
						|
			$default_eckd_size =~ s/default_eckd_size=//g;
 | 
						|
		}
 | 
						|
		elsif ( $_ =~ m/default_fba_size=/i ) {
 | 
						|
			$default_fba_size = $_;
 | 
						|
			$default_fba_size =~ s/default_fba_size=//g;
 | 
						|
		}
 | 
						|
		
 | 
						|
		# Get profile disk pool (default)
 | 
						|
		elsif ( $_ =~ 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, "$profile configuration for disk pool and size does not exist.  Using default configuration." );
 | 
						|
		
 | 
						|
		$disk_pool = $default_disk_pool;
 | 
						|
		$eckd_size = $default_eckd_size;
 | 
						|
		$fba_size = $default_fba_size;
 | 
						|
	}
 | 
						|
 | 
						|
	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 );
 | 
						|
	if (!$base_digit) {
 | 
						|
		println( $callback, "(Error) Failed to generate node name" );
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	
 | 
						|
	my $userid = $node;
 | 
						|
 | 
						|
	# Set node definitions
 | 
						|
	# Also put node into all group
 | 
						|
	if ($group eq 'all') {
 | 
						|
		$out = `mkdef -t node -o $node userid=$userid hcp=$hcp mgt=zvm groups=$group`;
 | 
						|
	} else {
 | 
						|
		# Put node in all group
 | 
						|
		$out = `mkdef -t node -o $node userid=$userid hcp=$hcp mgt=zvm groups=$group,all`;
 | 
						|
	}	
 | 
						|
	println( $callback, "$out" );
 | 
						|
 | 
						|
	# Set nodetype definitions
 | 
						|
	$out = `chtab node=$node 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 and DNS
 | 
						|
	`makehosts`;
 | 
						|
	`makedns`;
 | 
						|
 | 
						|
	# 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 = `ssh $hcp "sed $default_direct -e s/LXUSR/$userid/g" > /tmp/$node-direct.txt`;
 | 
						|
	$out = `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 = $data->{'dasd'}->{'devices'}->{'listentry'};
 | 
						|
		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 = `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 = `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 DHCP
 | 
						|
	`makedhcp -a`;
 | 
						|
 | 
						|
	# Toggle node power so COMMAND SET will get executed
 | 
						|
	`rpower $node on`;
 | 
						|
	`rpower $node off`;
 | 
						|
 | 
						|
	# Punch kernel, initrd, and ramdisk to node reader
 | 
						|
	$out = `nodeset $node install`;
 | 
						|
	println( $callback, "$out" );
 | 
						|
	if ( $out =~ m/Error/i ) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	# IPL reader and begin installation
 | 
						|
	$out = `rnetboot $node ipl=00C`;
 | 
						|
	println( $callback, "$out" );
 | 
						|
	if ( $out =~ m/Error/i ) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	
 | 
						|
	# Configure Ganglia monitoring
 | 
						|
	$out = `moncfg gangliamon $node -r`;
 | 
						|
	
 | 
						|
	# Show node information, e.g. IP, hostname, and root password
 | 
						|
	$out = `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 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
 | 
						|
					
 | 
						|
	# Are there nodes in this group already?
 | 
						|
	# If so, use the existing nodes as a base
 | 
						|
	my $out = `nodels $group`;
 | 
						|
	@args = split( /\n/, $out );
 | 
						|
	foreach (@args) {
 | 
						|
		$_ =~ s/$hostname_regex/$1/g;
 | 
						|
 | 
						|
		# Take the greatest digit
 | 
						|
		if ( int($_) > $base_digit ) {
 | 
						|
			$base_digit = int($_);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	# +1 to base digit to obtain next hostname
 | 
						|
	$base_digit = $base_digit + 1;
 | 
						|
	
 | 
						|
	# 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 (`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 = `tabdump nodetype -w nodetype.comments=~"owner:$owner"`;
 | 
						|
	my @tmp = split( /\n/, $out );
 | 
						|
	my $usrVM = scalar(@tmp) - 1;
 | 
						|
	
 | 
						|
	$out = `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 /opt/zhcp/conf/default.conf on zHCP
 | 
						|
	#	pool = POOL3
 | 
						|
	#	eckd_size = 10016
 | 
						|
	my $disk_pool;
 | 
						|
	my $default_conf   = '/opt/zhcp/conf/default.conf';
 | 
						|
	my $default_direct = '/opt/zhcp/conf/profiles/default.direct';
 | 
						|
 | 
						|
	# Exit if default.conf does not exist
 | 
						|
	if ( !(`ssh $hcp "test -e $default_conf && echo Exists"`) ) {
 | 
						|
		println( $callback, '(Error) $default_conf does not exists' );
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	# Exit if default.direct does not exist
 | 
						|
	if ( !(`ssh $hcp "test -e $default_direct && echo Exists"`) ) {
 | 
						|
		println( $callback, '(Error) $default_direct does not exists' );
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	$out = `ssh $hcp "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 $userid = $node;
 | 
						|
		
 | 
						|
	# Set node definitions
 | 
						|
	$out = `mkdef -t node -o $node userid=$userid hcp=$hcp mgt=zvm groups=$group`;
 | 
						|
	println( $callback, "$out" );
 | 
						|
 | 
						|
	# Set nodetype definitions
 | 
						|
	$out = `chtab node=$node 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 and DNS
 | 
						|
	`makehosts`;
 | 
						|
	`makedns`;
 | 
						|
 | 
						|
	# Update DHCP
 | 
						|
	`makedhcp -a`;
 | 
						|
	println( $callback, "hosts table, DHCP, and DNS updated" );
 | 
						|
 | 
						|
	# Clone virtual machine	
 | 
						|
	$out = `mkvm $node $src_node pool=$disk_pool`;
 | 
						|
	println( $callback, "$out" );
 | 
						|
	if ( $out =~ m/Error/i || $out =~ m/Failed/i ) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	
 | 
						|
	# Configure Ganglia monitoring
 | 
						|
	$out = `moncfg gangliamon $node -r`;
 | 
						|
	
 | 
						|
	# Show node information, e.g. IP, hostname, and root password
 | 
						|
	$out = `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 );
 | 
						|
	println( $callback, "$node: $ip" );
 | 
						|
}
 | 
						|
 | 
						|
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" } );
 | 
						|
}
 | 
						|
1; |