# Copyright (c) 2008..2014 - OpenSLX GmbH # # This program is free software distributed under the GPL version 2. # See http://openslx.org/COPYING # # If you have any feedback please consult http://openslx.org/feedback and # send your suggestions, praise, or complaints to feedback@openslx.org # # General information about OpenSLX can be found at http://openslx.org/ # ----------------------------------------------------------------------------- # vmware.pm # - declares necessary information for the vmware plugin # ----------------------------------------------------------------------------- package OpenSLX::OSPlugin::vmware; use strict; use warnings; use base qw(OpenSLX::OSPlugin::Base); use File::Basename; use File::Path; use OpenSLX::Basics; use OpenSLX::Utils; use OpenSLX::DistroUtils; sub new { my $class = shift; my $self = { name => 'vmware', }; return bless $self, $class; } sub getInfo { my $self = shift; return { description => unshiftHereDoc(<<' End-of-Here'), Module for enabling services of VMware Inc. on an OpenSLX stateless client. This plugin uses an installation of VMware within the cloned system. End-of-Here precedence => 70, required => [ qw( desktop ) ], }; } sub getAttrInfo { # returns a hash-ref with information about all attributes supported # by this specific plugin my $self = shift; # This default configuration will be added as attributes to the default # system, such that it can be overruled for any specific system by means # of slxconfig. return { # attribute 'active' is mandatory for all plugins 'vmware::active' => { applies_to_systems => 1, applies_to_clients => 1, description => unshiftHereDoc(<<' End-of-Here'), should the 'vmware'-plugin be executed during boot? End-of-Here content_regex => qr{^(0|1)$}, content_descr => '1 means active - 0 means inactive', default => '1', }, # attribute 'imagesrc' defines where we can find vmware images 'vmware::imagesrc' => { applies_to_systems => 1, applies_to_clients => 1, description => unshiftHereDoc(<<' End-of-Here'), Where do we store our vmware images? NFS? Filesystem? End-of-Here #TODO: check if the input is valid #content_regex => qr{^(0|1)$}, content_descr => 'Allowed values: path or URI', default => '', }, # attribute 'bridge' defines if bridged network mode should be # switched on 'vmware::bridge' => { applies_to_systems => 1, applies_to_clients => 1, description => unshiftHereDoc(<<' End-of-Here'), Should the bridging (direct access of the vmware clients to the ethernet the host is connected to) be enabled End-of-Here content_regex => qr{^(0|1)$}, content_descr => 'Allowed values: 0 or 1', default => '1', }, # attribute 'vmnet1' defines if the host connection network mode # should be switched on and NAT should be enabled 'vmware::vmnet1' => { applies_to_systems => 1, applies_to_clients => 1, description => unshiftHereDoc(<<' End-of-Here'), Format ServerIP/Netprefix without NAT Format ServerIP/Netprefix,NAT enables NAT/Masquerading End-of-Here #TODO: check if the input is valid #content_regex => qr{^(0|1)$}, content_descr => 'Allowed value: IP/Prefix[,NAT]', default => '192.168.101.1/24,NAT', }, # attribute 'vmnet8' defines if vmware specific NATed network mode # should be switched on 'vmware::vmnet8' => { applies_to_systems => 1, applies_to_clients => 1, description => unshiftHereDoc(<<' End-of-Here'), Format ServerIP/Netprefix. Last octet will be omitted End-of-Here #TODO: check if the input is valid #content_regex => qr{^(0|1)$}, content_descr => 'Allowed value: IP/Prefix. Last octet will be omitted', default => '192.168.102.x/24', }, # attribute 'kind' defines which set of VMware binaries should be # activated ('local' provided with the main installation set). 'vmware::kind' => { applies_to_vendor_os => 0, applies_to_systems => 1, applies_to_clients => 1, description => unshiftHereDoc(<<' End-of-Here'), Which set of VMware binaries to use: installed (local)? (Only pre-installed in the clone versions will work) End-of-Here # only allow the supported ones content_regex => qr{^(local)$}, content_descr => 'Allowed values: local', #TODO: what if we don't have a local installation. default # is still local. Someone has a clue how to test # it and change the default value? default => 'local', }, ## ## only stage1 setup options: different kinds to setup 'vmware::local' => { applies_to_vendor_os => 1, applies_to_system => 0, applies_to_clients => 0, description => unshiftHereDoc(<<' End-of-Here'), Set's up stage1 configuration for a local installed vmplayer or vmware workstation End-of-Here content_regex => qr{^(1|0)$}, content_descr => '1 means active - 0 means inactive', default => '1', }, # ** set of attributes for the installation of VM Workstation/Player # versions. More than one package could be installed in parallel. # To be matched to/triggerd by 'vmware::kind' }; } sub preInstallationPhase() { my $self = shift; my $info = shift; $self->{pluginRepositoryPath} = $info->{'plugin-repo-path'}; $self->{pluginTempPath} = $info->{'plugin-temp-path'}; $self->{openslxBasePath} = $info->{'openslx-base-path'}; $self->{openslxConfigPath} = $info->{'openslx-config-path'}; $self->{attrs} = $info->{'plugin-attrs'}; $self->{vendorOsPath} = $info->{'vendor-os-path'}; my $pkgpath = $self->{attrs}->{'vmware::pkgpath'}; my $local = $self->{attrs}->{'vmware::local'}; if ($local == 0) { print "\n\n * At least one kind needs to get installed/activated:\n"; print " vmware::local=1 or\n"; print " * vmware plugin was not installed!\n\n"; exit 1; } } sub installationPhase { my $self = shift; my $info = shift; $self->{pluginRepositoryPath} = $info->{'plugin-repo-path'}; $self->{pluginTempPath} = $info->{'plugin-temp-path'}; $self->{openslxBasePath} = $info->{'openslx-base-path'}; $self->{openslxConfigPath} = $info->{'openslx-config-path'}; $self->{attrs} = $info->{'plugin-attrs'}; # copy common part of run-virt.include to the appropriate place for # inclusion in stage4 copyFile("$self->{openslxBasePath}/lib/plugins/vmware/files/run-virt.include", "$self->{pluginRepositoryPath}/"); # kinds we will configure and install # TODO: write a list of installed/setted up and check it in stage3 # this will avoid conflict of configured vmware version in # stage3 which are not setted up or installed in stage1 if ($self->{attrs}->{'vmware::local'} == 1) { $self->_localInstallation(); } ## prepration for our faster wrapper script # rename the default vmplayer script and create a link. # uninstall routine takes care about plugin remove. # stage3 copys our own wrapper script if (-e "/usr/bin/vmplayer" && ! -e "/usr/bin/vmplayer.slx-back") { rename("/usr/bin/vmplayer", "/usr/bin/vmplayer.slx-bak"); } # the same with vmware, if ws is installed if (-e "/usr/bin/vmware" && ! -e "/usr/bin/vmware.slx-bak") { rename("/usr/bin/vmware", "/usr/bin/vmware.slx-bak"); } } sub removalPhase { my $self = shift; my $info = shift; # restore old start scripts - to be discussed my @files = qw( vmware vmplayer ); foreach my $file (@files) { if (-e "/usr/bin/$file.slx-bak") { unlink("/usr/bin/$file"); rename("/usr/bin/$file.slx-bak", "/usr/bin/$file"); } } return; } sub checkStage3AttrValues { my $self = shift; my $stage3Attrs = shift; my $vendorOSAttrs = shift; my @problems; my $vm_kind = $stage3Attrs->{'vmware::kind'} || ''; my $vmimg = $stage3Attrs->{'vmware::imagesrc'} || ''; if ($vm_kind eq 'local' && ! -x "/usr/lib/vmware/bin/vmplayer") { push @problems, _tr( "No local executeable installation of vmware found! Using it as virtual machine wouldn't work!" ); } if ($vm_kind eq 'local' && ! -d "/opt/openslx/plugin-repo/vmware/local") { push @problems, _tr( "local vmware installation not configured by slxos-plugin!" ); } return if !@problems; return \@problems; } ####################################### ## local, non-general OpenSLX functions ####################################### # Write the runlevelscript # usage: _writeRunlevelScript("$vmpath", "$kind") sub _writeRunlevelScript { my $self = shift; my $vmpath = shift; my $kind = shift; my $initfile = newInitFile(); my $script = ""; my $modpath = ""; my $modlist = ""; # vmpath is to be redefined here ... if ($kind =~ /local*/) { $vmpath = ""; $modpath = "/lib/modules/\$(uname -r)/misc"; } elsif ($kind =~ /vmpl*/) { $vmpath = "/opt/openslx/plugin-repo/vmware/${kind}"; $modpath = "${vmpath}/vmroot/modules"; } $initfile->setName("vmware-slx"); $initfile->setDesc("Setup environment for VMware Workstation or Player ($kind)."); # functions ... $script = unshiftHereDoc(<<" End-of-Here"); # Create a special tempfs directory mkdir -m 1777 -p /tmp/vmware # Don't mount special tempfs, when using local harddrive for /tmp [ ! -n "\$(cat /proc/mounts |grep ' /tmp '|grep '/dev/sd')" ] \\ && mount -t tmpfs -o size=180%,mode=1777 tmpfs /tmp/vmware End-of-Here $initfile->addFunction( "tempdir", "$script" ); $modlist = "vmnet vmmon "; $script = unshiftHereDoc(<<" End-of-Here"); # VMplayer common stuff insmod ${modpath}/vmmon.ko || return 1 insmod ${modpath}/vmnet.ko || return 1 End-of-Here if ($kind eq 'local3X' || $kind eq 'local4X' || $kind eq 'local5X' || $kind eq 'local6X') { $script .= unshiftHereDoc(<<" End-of-Here"); # Newer VMplayers specific stuff insmod ${modpath}/vmci.ko insmod ${modpath}/vmblock.ko insmod ${modpath}/vsock.ko End-of-Here $modlist .= "vsock vmci vmblock"; } elsif ($kind eq 'local20' || $kind eq 'local25') { $script .= unshiftHereDoc(<<" End-of-Here"); # VMplayer 2.X specific stuff insmod ${modpath}/vmblock.ko End-of-Here $modlist .= "vmblock"; } $initfile->addFunction( "load_modules", "$script" ); $initfile->addFunction( "unload_modules", "rmmod $modlist 2>/dev/null" ); # vmnet0,1,8 (bridge, nat, host-only) interface definition $script = unshiftHereDoc(<<" End-of-Here"); # let point the path directly to the directory where the binary lives location="$vmpath/usr/bin" if [ -n "\$vmnet0" ] ; then # the path might be directly point to the plugin dir End-of-Here if ($kind eq 'local10' || $kind eq 'local20') { $script .= " \$location/vmnet-bridge -d /var/run/vmnet-bridge-0.pid /dev/vmnet0 eth0\n"; } else { $script .= " \$location/vmnet-bridge -d /var/run/vmnet-bridge-0.pid -n 0\n"; } $script .= unshiftHereDoc(<<" End-of-Here"); fi if [ -n "\$vmnet1" ] ; then \$location/vmnet-netifup -d /var/run/vmnet-netifup-vmnet1.pid \\ /dev/vmnet1 vmnet1 ip addr add \$vmnet1 dev vmnet1 ip link set vmnet1 up if [ -n "\$vmnet1nat" ] ; then echo "1" >/proc/sys/net/ipv4/conf/vmnet1/forwarding 2>/dev/null echo "1" >/proc/sys/net/ipv4/conf/br0/forwarding 2>/dev/null #iptables -A -s vmnet1 -d br0 fi /opt/openslx/rootfs/usr/sbin/udhcpd \\ -S /etc/vmware/udhcpd/udhcpd-vmnet1.conf fi if [ -n "\$vmnet8" ] ; then \$location/vmnet-netifup -d /var/run/vmnet-netifup-vmnet8.pid \\ /dev/vmnet8 vmnet8 ip addr add \$vmnet8 dev vmnet8 ip link set vmnet8 up echo "1" >/proc/sys/net/ipv4/conf/vmnet8/forwarding 2>/dev/null echo "1" >/proc/sys/net/ipv4/conf/br0/forwarding 2>/dev/null iptables -t nat -A POSTROUTING -o br0 -j MASQUERADE # /etc/vmware/vmnet-natd-8.mac simply contains a mac like 00:50:56:F1:30:50 \$location/vmnet-natd -d /var/run/vmnet-natd-8.pid \\ -m /etc/vmware/vmnet-natd-8.mac -c /etc/vmware/nat.conf 2>/dev/null # or logfile /opt/openslx/rootfs/usr/sbin/udhcpd \\ -S /etc/vmware/udhcpd/udhcpd-vmnet8.conf fi End-of-Here $initfile->addFunction( "vmnetif", "$script" ); # vmblock for handling e.g. removable USB devices $script = unshiftHereDoc(<<" End-of-Here"); # let point the path directly to the directory where the binary lives $vmpath/usr/bin/vmware-usbarbitrator End-of-Here $initfile->addFunction( "vmblock", "$script" ); $script = unshiftHereDoc(<<" End-of-Here"); # include default directories . /etc/opt/openslx/openslx.conf # load the configuration file . \${OPENSLX_DEFAULT_CONFDIR}/plugins/vmware/vmware.conf # hack to access the first serial/parallel port chmod a+rw /dev/ttyS0 chmod a+rw /dev/parport0 tempdir load_modules vmnetif End-of-Here # start the USB arbitrator on higher VMware/player versions (3.0+) if ($kind eq 'local3X' || $kind eq 'local4X' || $kind eq 'local5X' || $kind eq 'local6X') { $script .= unshiftHereDoc(<<" End-of-Here"); vmblock End-of-Here } $initfile->addToCase( "start", "$script" ); $script = unshiftHereDoc(<<" End-of-Here"); killall vmnet-netifup vmnet-natd vmnet-bridge vmware vmplayer \\ vmware-tray vmnet-dhcpd 2>/dev/null # might take a while until all services are shut down sleep 1 umount -l /tmp/vmware 2>/dev/null unload_modules End-of-Here $initfile->addToCase( "stop", "$script" ); $initfile->addToCase( "status", "vmstatus" ); $initfile->addToCase( "restart", "\$0 stop && \$0 start" ); my $distro = (split('-',$self->{'os-plugin-engine'}->distroName()))[0]; my $runlevelscript = getInitFileForDistro($initfile, $distro); # todo: because we dont have distribution or version dependend # init scripts we could put it directly into /etc/init.d... spitFile("$self->{'pluginRepositoryPath'}/vmware-slx", $runlevelscript); } # writes the wrapper script for vmware workstation and player, depending # on the flag. If player: just player wrapper, if ws: ws+player wrapper # usage: _writeWrapperScript("$vmpath", "$kind", "player") # _writeWrapperScript("$vmpath", "$kind", "ws") sub _writeWrapperScript { my $self = shift; my $vmpath = shift; my $kind = shift; my $type = shift; my @files; if ("$type" eq "ws") { @files = qw(vmware vmplayer); } else { @files = qw(vmplayer); } foreach my $file (@files) { # create our own simplified version of the vmware and player wrapper # Depending on the configured kind it will be copied in stage3 # because of tempfs of /var but not /usr we link the file # to /var/..., where we can write in stage3 my $script = unshiftHereDoc(<<" End-of-Here"); #!/bin/sh # written by OpenSLX-plugin 'vmware' in Stage1 # radically simplified version of the original script $file by VMware Inc. End-of-Here # kinda ugly and we only need it for local. Preserves errors if ($kind ne "local") { $script .= unshiftHereDoc(<<" End-of-Here"); export LD_LIBRARY_PATH=$vmpath/lib export GDK_PIXBUF_MODULE_FILE=$vmpath/libconf/etc/gtk-2.0/gdk-pixbuf.loaders export GTK_IM_MODULE_FILE=$vmpath/libconf/etc/gtk-2.0/gtk.immodules export FONTCONFIG_PATH=$vmpath/libconf/etc/fonts export PANGO_RC_FILE=$vmpath/libconf/etc/pango/pangorc # possible needed... but what are they good for? #export GTK_DATA_PREFIX= #export GTK_EXE_PREFIX= #export GTK_PATH= End-of-Here } $script .= unshiftHereDoc(<<" End-of-Here"); PREFIX=$vmpath # depends on the vmware location exec "\$PREFIX"'/lib/wrapper-gtk24.sh' \\ "\$PREFIX"'/lib' \\ "\$PREFIX"'/bin/$file' \\ "\$PREFIX"'/libconf' "\$@" End-of-Here # TODO: check if these will be overwritten if we have more as # local defined (add the version/type like local, ...) # then we have a lot of files easily distinguishable by there suffix spitFile("$self->{'pluginRepositoryPath'}/$kind/$file", $script); chmod 0755, "$self->{'pluginRepositoryPath'}/$kind/$file"; } } sub _writeVmwareConfigs { my $self = shift; my $kind = shift; my $vmpath = shift; my %versionhash = (vmversion => "", vmbuildversion => ""); my $vmversion = ""; my $vmbuildversion = ""; my $config = ""; %versionhash = _getVersion($vmpath); $config .= "version=\"".$versionhash{vmversion}."\"\n"; $config .= "buildversion=\"".$versionhash{vmbuildversion}."\"\n"; spitFile("$self->{'pluginRepositoryPath'}/$kind/vmware.conf", $config); chmod 0755, "$self->{'pluginRepositoryPath'}/$kind/vmware.conf"; $config = "libdir = \"$vmpath\"\n"; spitFile("$self->{'pluginRepositoryPath'}/$kind/config", $config); chmod 0755, "$self->{'pluginRepositoryPath'}/$kind/config"; } sub _getVersion { my $vmpath = shift; my $vmversion = ""; my $vmbuildversion = ""; my %versioninfo = (vmversion => "", vmbuildversion => ""); # get version information about installed vmplayer if (open(FH, "$vmpath/bin/vmplayer")) { $/ = undef; my $data = ; close FH; # depending on the installation it could differ and has multiple build # strings if ($data =~ m{[^\d\.](\d\.\d) build-(\d+)}) { $vmversion = $1; $vmbuildversion = $2; } #if ($data =~ m{(\d\.\d\.\d)}) { # $vmversion = $1; #} # else { TODO: errorhandling if file or string doesn't exist } # with vmplayer greater than 4.x the previous method doesn't work anymore # so we use strings tool to get at least the build number.. if ( ! defined $vmbuildversion || $vmbuildversion eq '') { $data = qx{strings $vmpath/bin/vmplayer}; if ($data =~ m{build-(\d+)}) { $vmbuildversion = $1; } } chomp($vmversion); chomp($vmbuildversion); $versioninfo{vmversion} = $vmversion; $versioninfo{vmbuildversion} = $vmbuildversion; } if ( ! defined $vmversion || $vmversion eq '' ) { if (open(FH, "strings /usr/lib/vmware/lib/libvmplayer.so/libvmplayer.so | grep -E \"^[1-9]\" |")) { $/ = undef; my $data = ; close FH; if ($data =~ m{(\d\.\d)}) { $vmversion = $1; } chomp($vmversion); $versioninfo{vmversion} = $vmversion; } } return %versioninfo; } ######################################################################## ## Functions, which setup the different environments (local, ws-v(5.5|6), ## player-v(1|2) ## Seperation makes this file more readable. Has a bigger benefit as ## one big copy function. Makes integration of new versions easier. ######################################################################## # local installation sub _localInstallation { my $self = shift; my $kind = "local"; my $vmpath = "/usr/lib/vmware"; my $vmbin = "/usr/bin"; my %versionhash = (vmversion => "", vmbuildversion => ""); my $vmversion = ""; my $vmbuildversion = ""; # if vmware ws is installed, vmplayer is installed, too. # we will only use vmplayer if (-e "/usr/lib/vmware/bin/vmplayer") { ## Get and write version information %versionhash = _getVersion($vmpath); $vmversion = $versionhash{vmversion}; $vmbuildversion = $versionhash{vmbuildversion}; # set version information # VMplayer 3.0, 3.1, 4.X, 5.X, 6.X Workstation 7.0, 7.1, 8.X, 9.X, 10.X if ($vmversion eq "3.0" || $vmversion eq "7.0" || $vmversion eq "3.1" || $vmversion eq "7.1") { $kind="local3X"; } elsif ($vmversion eq "4.0" || $vmversion eq "8.0") { $kind="local4X"; } elsif ($vmversion eq "5.0" || $vmversion eq "9.0") { $kind="local5X"; } elsif ($vmversion eq "6.0" || $vmversion eq "10.0") { $kind="local6X"; } # Create runlevel script depending on detected version $self->_writeRunlevelScript("$vmpath", "$kind"); # Create wrapper scripts, kind of localNN is set to local if ( $kind =~ /local*/ ) { $kind = "local"; } if (-e "/usr/lib/vmware/bin/vmware") { $self->_writeWrapperScript("$vmpath", "$kind", "ws"); } if (-e "/usr/lib/vmware/bin/vmplayer") { $self->_writeWrapperScript("$vmpath", "$kind", "player"); } # copy nvram file my $pluginFilesPath = "$self->{'openslxBasePath'}/lib/plugins/$self->{'name'}/files"; my @files = qw(nvram); foreach my $file (@files) { copyFile("$pluginFilesPath/$file", "$self->{'pluginRepositoryPath'}/$kind"); } } ## creating needed config /etc/vmware/config $self->_writeVmwareConfigs("$kind", "$vmpath"); } # The bridge configuration needs the bridge module to be present in early # stage3 sub suggestAdditionalKernelModules { my $self = shift; my $makeInitRamFSEngine = shift; my @suggestedModules; push @suggestedModules, qw( bridge ); return @suggestedModules; } 1;