diff --git a/xCAT-UI/xCAT-UI.spec b/xCAT-UI/xCAT-UI.spec index f32a837c0..0bd3cabde 100644 --- a/xCAT-UI/xCAT-UI.spec +++ b/xCAT-UI/xCAT-UI.spec @@ -141,6 +141,8 @@ set -x then # Copy xCAT plugins to /opt/xcat/lib/perl/xCAT_plugin cp %{prefix}/ui/xcat/plugins/web.pm /opt/xcat/lib/perl/xCAT_plugin/ + cp %{prefix}/ui/xcat/plugins/webportal.pm /opt/xcat/lib/perl/xCAT_plugin/ + /bin/ln -s /opt/xcat/bin/xcatclientnnr /opt/xcat/bin/webportal /etc/init.d/xcatd restart # Copy php.ini file into /opt/xcat/ui and turn off output_buffering diff --git a/xCAT-UI/xcat/plugins/webportal.pm b/xCAT-UI/xcat/plugins/webportal.pm new file mode 100644 index 000000000..b2d124846 --- /dev/null +++ b/xCAT-UI/xcat/plugins/webportal.pm @@ -0,0 +1,342 @@ +# 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 + ); + + # 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]; + + # 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/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; + } + + my $out = `ssh $hcp "cat $default_conf"`; + my @tmp = split( /\n/, $out ); + foreach (@tmp) { + # Get disk pool + if ( $_ =~ m/pool =/i ) { + $disk_pool = $_; + $disk_pool =~ s/pool =//g; + } + + # Get disk size + elsif ( $_ =~ m/eckd_size =/i ) { + $eckd_size = $_; + $eckd_size =~ s/eckd_size =//g; + } + elsif ( $_ =~ m/fba_size = /i ) { + $fba_size = $_; + $fba_size =~ s/fba_size = //g; + } + } + + # 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]; + + 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, $base_digit) = gennodename( $callback, $group ); + my $userid = 'XCAT' . $base_digit; + + # 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.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 + + # Create XML object + my $xml = new XML::Simple; + + # Read XML file + my $data = $xml->XMLin($tmpl); + + my $type; + my $virt_addr; + 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 + + # 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; + } + + # 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. 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 ) = @_; + + my $base_digit = 0; + my $base_hostname; + my $hostname; + 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' ); + foreach (@results) { + + # It should return: |gpok(\d+)|10.1.100.($1+0)| + @args = split( /\|/, $_->{'ip'} ); + $base_hostname = $args[1]; + } + + # Are there nodes in this group already? + my $out = `nodels $group`; + @args = split( /\n/, $out ); + foreach (@args) { + $_ =~ s/$base_hostname/$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; + $base_hostname = substr( $hostname, 0, index( $hostname, '(\d+)' ) ); + $hostname = substr( $hostname, 0, index( $hostname, '(\d+)' ) ) . $base_digit; + + # Check if hostname is already used + while (`nodels $hostname`) { + # +1 to base digit to obtain next hostname + $base_digit = $base_digit + 1; + $hostname = $base_hostname . $base_digit; + } + + return ($hostname, $base_digit); +} +1;