# VMware MLN plugin written by Kyrre Begnum
# tested with VMware Server 1.0.5

my $VMWARE_PLUGIN_VERSION = 0.3;
my $VNET_START = 10;
my $VNET_STOP = 99;
my $VMWAREDIR = "/etc/mln/vmware";
my $MAPPING_FILE = "vnet_switch_mappings.dat";

sub vmware_version {
    out("VMware Server plugin version $VMWARE_PLUGIN_VERSION\n");
}


sub vmware_createFilesystem {
    my $hostname = $_[0]; 
    if ( getScalar("/host/$hostname/vmware")){
	out("VMware plugin is enabled.\nOnly file images and no resizing is suported\n");
	my $tem = getScalar("/host/$hostname/template");
	if ( stat("$TEMPLATEDIR/$tem") ){
	    out("Template found, copying:\n");
	    system("vmware-vdiskmanager -r $TEMPLATEDIR/$tem  -t 2 $PROJECT_PATH/$PROJECT/images/$hostname.vmdk");
	      
	}

	return 1;
    }
}

sub vmware_mountFilesystem {
    my $hostname = $_[0]; 
    if ( getScalar("/host/$hostname/vmware")){
	out("VMware plugin for mounting $hortname\n");
	system("mount -o loop,offset=32256 $PROJECT_PATH/$PROJECT/images/${hostname}-flat.vmdk $MOUNTDIR");
	system("df -h");
	return 1;
    }
}

sub vmware_createStartStopScripts { 
    my $hostname = $_[0]; 
    if ( getScalar("/host/$hostname/vmware")){
	
	my $bo = getScalar("/host/$hostname/boot_order");
	$bo = 99 unless $bo;
	out("creating: $PROJECT_PATH/$PROJECT/start_${bo}_$hostname.sh\n");
	open(START,">$PROJECT_PATH/$PROJECT/start_${bo}_$hostname.sh");
	print START "#!/bin/bash\n";
	print START "echo \"Starting $hostname...\"\n";
	print START "vmware-cmd $PROJECT_PATH/$PROJECT/${hostname}.vmx start trysoft\n";
	close(START);
	out("creating: $PROJECT_PATH/$PROJECT/stop_${bo}_$hostname.sh\n");
	open(STOP,">$PROJECT_PATH/$PROJECT/stop_${bo}_$hostname.sh");	

	print STOP "#!/bin/bash\n";
	print STOP "echo \"Stopping $hostname...\"\n";
	print STOP "vmware-cmd $PROJECT_PATH/$PROJECT/${hostname}.vmx stop trysoft\n";
	close(STOP);
	system("chmod +x $PROJECT_PATH/$PROJECT/start_${bo}_$hostname.sh");
	system("chmod +x $PROJECT_PATH/$PROJECT/stop_${bo}_$hostname.sh");
	vmware_writeVMXandRegister($hostname);
	return 1;

    }
}

sub vmware_importFilesystem {
    my $host = $_[0];
    my $project = $_[1];
    my $directory = $_[2];
    if ( getScalar("/host/$host/vmware")){
	system("cp -v  $directory/$host.vmdk $PROJECT_PATH/$project/images/$host.vmdk");
	system("cp -v $directory/${host}-flat.vmdk $PROJECT_PATH/$project/images/${host}-flat.vmdk ");
	return 1;
    }    
}

sub vmware_exportFilesystem {
    my $host = $_[0];
    my $project = $_[1];
    my $directory = $_[2];
    if ( getScalar("/host/$host/vmware")){
	system("cp -v $PROJECT_PATH/$project/images/$host.vmdk $directory");
	system("cp -v $PROJECT_PATH/$project/images/${host}-flat.vmdk $directory");
	return 1;
    }    
}

sub vmware_getImportExportFiles {
    my $host = $_[0];
    my $project = $_[1];
    my %targets; 
    if ( getScalar("/host/$host/vmware")){
	$targets{"$host.vmdk"} = "$PROJECT_PATH/$project/images/$host.vmdk";
	$targets{"${host}-flat.vmdk"} = "$PROJECT_PATH/$project/images/${host}-flat.vmdk";
	return %targets;
    }
}

sub vmware_writeVMXandRegister {
    my $hostname = $_[0]; 
    if ( getScalar("/host/$hostname/vmware")){
	my $memory = getScalar("/host/$hostname/memory");
	$memory = $DEFAULS{MEMORY} unless $memory;
	$memory =~ s/^(\d+)M/$1/;
	open(VMX,">$PROJECT_PATH/$PROJECT/$hostname.vmx");
	print VMX "#!/usr/bin/vmware\n";
	print VMX "# VMX file generated by MLN VMware plugin ($VMWARE_PLUGIN_VERSION)\n";
	print VMX "config.version = \"8\"\n";
	print VMX "virtualHW.version = \"4\"\n";
	print VMX "displayName = \"$hostname.$PROJECT\"\n";
	print VMX "# Floppy and USB are disabled\n";
	print VMX "usb.present = \"FALSE\"\n";
	print VMX "floppy0.present = \"FALSE\"\n";
	print VMX "floppy0.fileName = \"/dev/fd0\"\n\n";
	print VMX "guestOS = \"otherlinux\"\n";
	print VMX "nvram = \"bios.nvram\"\n";

	print VMX "scsi0.present = \"TRUE\"\n";
	print VMX "scsi0:0.present = \"TRUE\"\n";
	print VMX "scsi0:0.fileName = \"$PROJECT_PATH/$PROJECT/images/$hostname.vmdk\"\n";
	print VMX "scsi0:0.deviceType = \"disk\"\n";
	print VMX "scsi0:0.redo = \"\"\n";
	print VMX "scsi0.virtualDev = \"lsilogic\"\n";
	print VMX "memsize = \"$memory\"\n";
	print VMX "\n# NETWORKING:\n\n";
	my %ifs = getHash("/host/$hostname/network");
	if ( %ifs ){

	    foreach my $if (keys %ifs){
		my $device = $if;
		$device =~ s/eth/ethernet/;
		print "writing device $device\n";
		print VMX "$device.present = \"TRUE\"\n";
		if ( not $ifs{$if}{"switch"}){
		    print VMX "$device.connectionType = \"bridged\"\n";
		} else {
		    print VMX "$device.connectionType = \"custom\"\n";
		    my $mapping = vmware_getMapping("$ifs{$if}{'switch'}.$PROJECT");
		    print VMX "$device.vnet = \"$mapping\"\n";
		}
		print VMX "$device.addressType = \"generated\"\n";
	    }
	    
	}
	close(VMX);
	system("chmod +x $PROJECT_PATH/$PROJECT/$hostname.vmx");
	system("vmware-cmd -s register $PROJECT_PATH/$PROJECT/$hostname.vmx");
	return 1;
    }
}
    
sub vmware_removeHost {
    my $hostname = $_[0];
    my $project = $_[1];
    if ( getScalar("/host/$hostname/vmware") ){
	out("VMware: unregistering $hostname.$project\n");
	system("vmware-cmd -s unregister $PROJECT_PATH/$project/$hostname.vmx");
    }
    
}

sub vmware_checkIfUp {
    my $hostname = $_[0];
    my $project = $_[1];
    my $root = $_[2];
    if ( getScalar("/host/$hostname/vmware",$root) ){
#	print "VMware plugin\n";
	my @ps = `ps aux`;
	my $regex = "vmware-vmx -C $PROJECT_PATH\/$project\/$hostname.vmx";
#	print "reg: $regex\n";
	my @result = grep(/$regex/,@ps);
	if ( @result ){
	    return 1;
	}
	return -1;
    }
    
}

sub vmware_checkIfSwitchIsUp {
    my $switch = $_[0];
    my $project = $_[1];
    my $root = $_[2];
    if ( getScalar("/switch/$switch/vmware",$root) ){
#	print "VMware plugin\n";
	my $mapping = vmware_getMapping($switch . "." . $project);
	my @ps = `ps aux`;
	my $regex = "vmnet-bridge -d \/var\/run\/$mapping.pid";
	if ( grep(/$regex/,@ps)){
	    return 1;
	} else {
	    return -1;
	}
    }    
}

sub vmware_removeSwitch {
    my $name = $_[0];
    my $project = $_[1];
    my $root = $_[2];
#    print "vmware_removeSwitch called\n";
    if ( getScalar("/switch/$name/vmware")){
	out("VMware plugin acivated for switch\n");
	vmware_deleteMapping("$name.$project");
	return 1;
    }    
}

sub vmware_buildSwitch {
    my $name = $_[0];
    print "vmware_buildSwitch called:\n";
    if ( getScalar("/switch/$name/vmware")){
	out("VMware plugin acivated for switch\n");
	my $mapping = vmware_getNewMapping("$name.$PROJECT");
	my $number = $mapping;
	$number =~ s/\S+(\d\d)$/$1/;

	open( START, ">$PROJECT_PATH/$PROJECT/start_$name.sh" )
	  or die "Failed to open $PROJECT_PATH/$PROJECT/start_$name.sh\n";
	print START "#!/bin/bash\n";
	print START "if ! [ -e /dev/$mapping ]; then\n";
	print START "echo 'Creating device /dev/$mapping'\n";
	print START "mknod /dev/$mapping c 119 $number\n";
	print START "fi\n";
	
	print START "echo 'Starting switch $name.$PROJECT'\n";
	print START "vmnet-bridge -d /var/run/$mapping.pid /dev/$mapping $mapping\n";
	close(START);
	system("chmod +x $PROJECT_PATH/$PROJECT/start_$name.sh");
	open( STOP, ">$PROJECT_PATH/$PROJECT/stop_$name.sh" )
	  or die "Failed to open $PROJECT_PATH/$PROJECT/stop_$name.sh\n";
	print STOP "#!/bin/bash\n";
	print STOP "echo 'Stopping switch $name.$PROJECT'\n";
	print STOP "kill \$(cat /var/run/$mapping.pid)\n";
	close(STOP);
	system("chmod +x $PROJECT_PATH/$PROJECT/stop_$name.sh");
#	exit;
	return 1;

    }

}

sub vmware_dummy {
    my $hostname = $_[0]; 
    if ( getScalar("/host/$hostname/vmware")){
	
	return 1;
    }
}



sub vmware_getMapping {
    my $switch = $_[0];
    my @mappings;
    if ( stat("$VMWAREDIR/$MAPPING_FILE")){
	@mappings = `cat $VMWAREDIR/$MAPPING_FILE`;
	my $mapping; 
	foreach $mapping (@mappings){
	    if ( $mapping =~ /^(vmnet\d+) $switch\n/ ){
#	    print "returning $1\n"; 
	    return $1;
	    }

	}


    }
}

sub vmware_deleteMapping {
    my $switch = $_[0];
    my @mappings;
    if ( stat("$VMWAREDIR/$MAPPING_FILE")){
	@mappings = `cat $VMWAREDIR/$MAPPING_FILE`;
	open(MAPPING,">$VMWAREDIR/$MAPPING_FILE");
	my $mapping;
	foreach $mapping  (@mappings){
	    if ( not $mapping =~ /$switch\n/ ){
		print MAPPING "$mapping";
	    }
	}
	close(MAPPING);
    }
}

sub vmware_getNewMapping {
    
    my $switch = $_[0];
    if ( not stat("$VMWAREDIR")){
	system("mkdir $VMWAREDIR");
    }
    my @mappings;
    if ( stat("$VMWAREDIR/$MAPPING_FILE")){
	@mappings = `cat $VMWAREDIR/$MAPPING_FILE`;
    }
    # This loop searches for a free vnet to use for a switch
    my $vacant_net;
    for ( my $vnet = $VNET_START; $vnet <= $VNET_STOP; $vnet++ ){
	if ( not grep(/^vmnet$vnet/,@mappings )){
	    $vacant_net = $vnet; 
	    out("Vacant net found at vmnet$vnet\n");
	    push(@mappings,"vmnet$vnet $switch\n");
	    last;
	}
    }
    open(MAPPING,">$VMWAREDIR/$MAPPING_FILE");
    foreach (@mappings){
	print MAPPING "$_";
    }
    close(MAPPING);
    # check if the device node exists
    if ( not stat("/dev/vmnet$vacant_net")){
	out("creating /dev/vmnet$vacant_net\n");
	system("mknod --mode=600 /dev/vmnet$vacant_net c 119 $vacant_net");
    }
    
    return "vmnet$vacant_net";
    
}

1;