[pve-devel] r5107 - in qemu-server/pve2: . PVE/API2

svn-commits at proxmox.com svn-commits at proxmox.com
Fri Sep 10 09:54:17 CEST 2010


Author: dietmar
Date: 2010-09-10 07:54:17 +0000 (Fri, 10 Sep 2010)
New Revision: 5107

Modified:
   qemu-server/pve2/ChangeLog
   qemu-server/pve2/PVE/API2/QemuServer.pm
   qemu-server/pve2/QemuServer.pm
   qemu-server/pve2/nqm
Log:
	* QemuServer.pm: use a JSON Schema to describe all options. We can
	now auto-generate the complete API doc.

	* QemuServer.pm (parse_options_new): first try

	* QemuServer.pm (check_type): register paramater formats with
	PVE::JSONSchema::register_format()
	(check_type): raise exceptions if something fail



Modified: qemu-server/pve2/ChangeLog
===================================================================
--- qemu-server/pve2/ChangeLog	2010-09-10 07:43:40 UTC (rev 5106)
+++ qemu-server/pve2/ChangeLog	2010-09-10 07:54:17 UTC (rev 5107)
@@ -1,3 +1,16 @@
+2010-09-10  Proxmox Support Team  <support at proxmox.com>
+
+	* QemuServer.pm: use a JSON Schema to describe all options. We can
+	now auto-generate the complete API doc.
+
+	* QemuServer.pm (parse_options_new): first try
+
+2010-09-08  Proxmox Support Team  <support at proxmox.com>
+
+	* QemuServer.pm (check_type): register paramater formats with
+	PVE::JSONSchema::register_format()
+	(check_type): raise exceptions if something fail
+
 2010-09-07  Proxmox Support Team  <support at proxmox.com>
 
 	* nqm: temporary file to test new API - will replace 'qm' later.

Modified: qemu-server/pve2/PVE/API2/QemuServer.pm
===================================================================
--- qemu-server/pve2/PVE/API2/QemuServer.pm	2010-09-10 07:43:40 UTC (rev 5106)
+++ qemu-server/pve2/PVE/API2/QemuServer.pm	2010-09-10 07:54:17 UTC (rev 5107)
@@ -3,9 +3,12 @@
 use strict;
 use warnings;
 
+use PVE::INotify qw(read_file);
 use PVE::RESTHandler;
 use PVE::QemuServer;
 
+use Data::Dumper; # fixme: remove
+
 # API URLs
 #
 ## GET    /vms/
@@ -74,4 +77,34 @@
 
     }});
 
+__PACKAGE__->register_method ({
+    name => 'create_vm', 
+    path => '{node}', 
+    method => 'POST',
+    description => "Create new virtual machine.",
+    parameters => {
+    	additionalProperties => 0,
+	properties => PVE::QemuServer::json_config_properties(),
+    },
+    returns => { type => 'null'},
+    code => sub {
+	my ($param) = @_;
+
+	my $node = $param->{node};
+	delete $param->{node};
+
+	my $vmid = $param->{vmid};
+	delete $param->{vmid};
+
+	print "create $vmid on node $node\n";
+
+	my $storecfg = read_file('storagecfg');
+
+	my $opts = PVE::QemuServer::parse_options_new($storecfg, $param->{vmid}, $param, 1);
+
+	print Dumper($opts);
+
+	return undef;
+    }});
+
 1;

Modified: qemu-server/pve2/QemuServer.pm
===================================================================
--- qemu-server/pve2/QemuServer.pm	2010-09-10 07:43:40 UTC (rev 5106)
+++ qemu-server/pve2/QemuServer.pm	2010-09-10 07:54:17 UTC (rev 5107)
@@ -17,6 +17,8 @@
 use IPC::Open3;
 use Fcntl;
 use Sys::Syslog;
+use Storable qw(dclone);
+use PVE::Exception qw(raise raise_param_exc);
 use PVE::Storage;
 use PVE::Tools qw(run_command lock_file file_read_firstline);
 use PVE::INotify qw(read_file);
@@ -116,98 +118,343 @@
 my $pcisysfs = "/sys/bus/pci";
 
 my $default_memory = 512;
+my $default_min_memory = 16;
 
-my $confvars = {
-    onboot => 'bool',
-    autostart => 'bool',
-    reboot => 'bool',
-    lock => 'lockinfo',
-    cpulimit => 'natural',
-    cpuunits => 'natural',
-    memory => 'natural',
-    keyboard => 'lang',
-    name => 'string',
-    description => 'string',
-    ostype => 'ostype',
-    boot => 'boot',
-    bootdisk => 'bootdisk',
-    smp => 'natural',
-    sockets => 'natural',
-    cores => 'natural',
-    acpi => 'bool',
-    kvm => 'bool',
-    tdf => 'bool',
-    localtime => 'bool',
-    freeze => 'bool',
-    vlanu => 'vlan',
-    vga => 'vgatype',
-    hostusb => 'hostusb',
-    hostpci => 'hostpci',
-    serial => 'serial',
-    parallel => 'parallel',
-    startdate => 'startdate',
-    args => 'string',
-    tablet => 'bool',
-    migrate_speed => 'natural',
-    migrate_downtime => 'natural',
+my $keymaphash = PVE::Tools::kvmkeymaps();
 
-    #cpu => 'string',
-    #machine => 'string',
-    #fda => 'file',
-    #fdb => 'file',
-    #mtdblock => 'file',
-    #sd => 'file',
-    #pflash => 'file',
-    #snapshot => 'bool',
-    #bootp => 'file',
-    ##tftp => 'dir',
-    ##smb => 'dir',
-    ##usb
-    ##usbdevice
-    #kernel => 'file',
-    #append => 'string',
-    #initrd => 'file',
+my $confdesc = {
+    onboot => {
+	optional => 1,
+	type => 'boolean',
+	description => "Specifies whether a VM will be started during system bootup. Default is read from global configuration file.",
+	default => 0,
+    },
+    autostart => {
+	optional => 1,
+	type => 'boolean',
+	description => "Automatic restart after crash (currently ignored). Default is read from global configuration file.",
+	default => 0,
+    },
+    reboot => {
+	optional => 1,
+	type => 'boolean',
+	description => "Allow reboot (default=yes). If set to 'no' the VM exit on reboot.",
+    },
+    lock => {
+	optional => 1,
+	type => 'string',
+	description => "Lock/unlock the VM.",
+	enum => [qw(migrate backup)],
+    },
+    cpulimit => {
+	optional => 1,
+	type => 'integer',
+	description => "Limit of CPU usage in per cent. Note if the computer has 2 CPUs, it has total of 200% CPU time. Default CPU limit is 0 (no CPU limit).\n\nNOTE: This option is currently ignored.",
+	minimum => 0,
+    },
+    cpuunits => {
+	optional => 1,
+	type => 'integer',
+	description => "CPU weight for a VM. Argument is used in the kernel fair scheduler. The larger the number is, the more CPU time this VM gets. Number is relative to weights of all the other running VMs.\n\nNOTE: You can disable fair-scheduler configuration by setting this to 0. Default is read from global configuration file.",
+	minimum => 0,
+	maximum => 500000,
+	default => 1000,
+    },
+    memory => {
+	optional => 1,
+	type => 'integer',
+	description => "Amount of RAM for the VM in MB. Default is read from global configuration file.",
+	minimum => $default_min_memory,
+	default => $default_memory,
+    },
+    keyboard => {
+	optional => 1,
+	type => 'string',
+	description => "Keybord layout for vnc server. Default is read from global configuration file.",
+	enum => [ keys %$keymaphash ],
+    },
+    name => {
+	optional => 1,
+	type => 'string',
+	description => "Set a name for the VM. Only used on the configuration web interface.",
+    },
+    description => {
+	optional => 1,
+	type => 'string',
+	description => "Description for the VM. Only used on the configuration web interface.",
+    },
+    ostype => {
+	optional => 1,
+	type => 'string',
+        enum => [qw(other wxp w2k w2k3 w2k8 wvista l24 l26)],
+	description => <<EODESC,
+Used to enable special optimization/features for specific
+operating systems:
 
-    ##soundhw => 'string',
+other  => unspecified OS
+wxp    => Microsoft Windows XP
+w2k    => Microsoft Windows 2000
+w2k3   => Microsoft Windows 2003
+w2k8   => Microsoft Windows 2008
+wvista => Microsoft Windows Vista
+l24    => Linux 2.4 Kernel
+l26    => Linux 2.6 Kernel
+
+other|l24|l26             ... no special behaviour
+wxp|w2k|w2k3|w2k8|wvista  ... use --localtime switch
+EODESC
+    },
+    boot => {
+	optional => 1,
+	type => 'string',
+	description => "Boot on floppy (a), hard disk (c), CD-ROM (d), or network (n). Default is 'cad' (disk, floppy, cdrom)",
+	pattern => '[acdn]{1,3}',
+    },
+    bootdisk => {
+	optional => 1,
+	type => 'string', format => 'pve-qm-bootdisk',
+	description => "Enable booting from specified disk.",
+	pattern => '(ide|scsi|virtio)\d+',
+    },
+    smp => {
+	optional => 1,
+	type => 'integer',
+	description => "The number of CPUs (default=1). Please use option -sockets instead.",
+	minimum => 1,
+    },
+    sockets => {
+	optional => 1,
+	type => 'integer',
+	description => "The number of CPU sockets (default=1).",
+	minimum => 1,
+    },
+    cores => {
+	optional => 1,
+	type => 'integer',
+	description => "The number of cores per socket (default=1)",
+	minimum => 1,
+    },
+    acpi => {
+	optional => 1,
+	type => 'boolean',
+	description => "Enable/disable ACPI (default=yes).",
+    },
+    kvm => {
+	optional => 1,
+	type => 'boolean',
+	description => "Enable/disable KVM hardware virtualization (default=yes).",
+    },
+    tdf => {
+	optional => 1,
+	type => 'boolean',
+	description => "Enable/disable time drift fix. Default is read from global configuration file.",
+	default => 1,
+    },
+    localtime => { 
+	optional => 1,
+	type => 'boolean',
+	description => "Set the real time clock to local time.",
+    },
+    freeze => {
+	optional => 1,
+	type => 'boolean',
+	description => "Freeze CPU at startup (use 'c' monitor command to start execution).",
+    },
+    vlanu => {
+	optional => 1,
+	type => 'string', format => 'pve-qm-vlan',
+	typetext => "MODEL=XX:XX:XX:XX:XX:XX { , MODEL=YY:YY:YY:YY:YY:YY  }",
+	description => "Same as vlan[n], but vlanu uses user mode network stack (NAT).
+Provides DHCP and DNS services. The following addresses are used:\n\n10.0.2.2   Gateway\n10.0.2.3   DNS Server\n10.0.2.4   SMB Server\n\nThe DHCP server assign addresses to the guest starting from 10.0.2.15.",
+    },
+    vga => {
+	optional => 1,
+	type => 'string',
+	description => "Select VGA type. Default is a Cirrus Logic GD5446 PCI VGA card ('cirrus'). If you want to use high resolution modes (>= 1280x1024x16) then you should use option 'std' or 'vmware'. Default is read from global configuration file.",
+	enum => [qw(std cirrus vmware)],
+	default => 'cirrus',
+    },
+    hostusb => {
+	optional => 1,
+	type => 'string', format => 'pve-qm-hostusb',
+	typetext => "HOSTUSBDEVICE { , HOSTUSBDEVICE }",
+	description => <<EODESCR,
+Map host usb devices. HOSTUSBDEVICE syntax is:
+
+'bus.addr' (decimal numbers) or 
+'vendor_id:product_id' (hexadeciaml numbers)
+
+You can use the 'lsusb' command to list existing usb devices (or take a look at '/proc/bus/usb/devices'.
+
+Note: This option allows direct access to host hardware. So it is no longer possible to migrate such machines - use with special care.
+EODESCR
+    },
+    hostpci => {
+	optional => 1,
+        type => 'string', format => 'pve-qm-hostpci',
+	typetext => "HOSTPCIDEVICE { , HOSTPCIDEVICE }",
+	description => <<EODESCR,
+Map host pci devices. HOSTPCIDEVICE syntax is:
+
+'bus:dev.func' (hexadecimal numbers)
+
+You can us the 'lspci' command to list existing pci devices.
+
+Note: This option allows direct access to host hardware. So it is no longer possible to migrate such machines - use with special care.
+
+Experimental: user reported problems with this option.
+EODESCR
+    },
+    serial => {
+	optional => 1,
+	type => 'string', format => 'pve-qm-serial',
+	typetext => "SERIALDEVICE { , SERIALDEVICE }",
+	description =>  <<EODESCR,
+Map host serial devices. SERIALDEVICE syntax is /dev/ttyS* 
+
+Note: This option allows direct access to host hardware. So it is no longer possible to migrate such machines - use with special care.
+
+Experimental: user reported problems with this option.
+EODESCR
+    },
+    parallel => {
+	optional => 1,
+	type => 'string', format => 'pve-qm-parallel',
+	typetext => "PARALLELDEVICE { , PARALLELDEVICE }",
+	description =>  <<EODESCR,
+Map host parallel devices. PARALLELDEVICE syntax is /dev/parport* 
+
+Note: This option allows direct access to host hardware. So it is no longer possible to migrate such machines - use with special care.
+
+Experimental: user reported problems with this option.
+EODESCR
+    },
+    startdate => {
+	optional => 1,
+	type => 'string', 
+	typetext => "(now | YYYY-MM-DD | YYYY-MM-DDTHH:MM:SS)",
+	description => "Set the initial date of the real time clock. Valid format for date are: 'now' or '2006-06-17T16:01:21' or '2006-06-17'. The default value is 'now'.",
+	pattern => '(now|\d{4}-\d{1,2}-\d{1,2}(T\d{1,2}:\d{1,2}:\d{1,2})?)',	    
+    },
+    args => {
+	optional => 1,
+	type => 'string',
+	description => <<EODESCR,
+Note: this option is for experts only. It allows you to pass arbitrary arguments to kvm, for example:
+
+args: -no-reboot -no-hpet
+EODESCR
+    },
+    tablet => {
+	optional => 1,
+	type => 'boolean',
+	default => 1,
+	description => "Enable/disable the usb tablet device. This device is usually needed to allow absolute mouse positioning. Else the mouse runs out of sync with normal vnc clients. If you're running lots of console-only guests on one host, you may consider disabling this to save some context switches. Default is read from global configuration file.",
+    },
+    migrate_speed => {
+	optional => 1,
+	type => 'integer',
+	description => "Set maximum speed (in MB/s) for migrations. Default is read from global configuration file. Value 0 is no limit.",
+	minimum => 0,
+	default => 0,
+    },
+    migrate_downtime => {
+	optional => 1,
+	type => 'integer',
+	description => "Set maximum tolerated downtime (in seconds) for migrations. Default is read from global configuration file.",
+	minimum => 0,
+	default => 1,
+    },
+    cdrom => {
+	optional => 1,
+	type => 'string', format => 'pve-qm-drive',
+	typetext => 'volume',
+	description => "This is an alias for option -ide2",
+    },
 };
 
+# what about other qemu settings ?
+#cpu => 'string',
+#machine => 'string',
+#fda => 'file',
+#fdb => 'file',
+#mtdblock => 'file',
+#sd => 'file',
+#pflash => 'file',
+#snapshot => 'bool',
+#bootp => 'file',
+##tftp => 'dir',
+##smb => 'dir',
+##usb
+#kernel => 'file',
+#append => 'string',
+#initrd => 'file',
+##soundhw => 'string',
+
+
 my $MAX_IDE_DISKS = 4;
 my $MAX_SCSI_DISKS = 16;
 my $MAX_VIRTIO_DISKS = 16;
 my $MAX_VLANS = 4095;
 
+my $nic_model_list = ['rtl8139', 'ne2k_pci', 'e1000',  'pcnet',  'virtio',
+		      'ne2k_isa', 'i82551', 'i82557b', 'i82559er'];
+my $nic_model_list_txt = join (' ', sort @$nic_model_list);
+
+my $vlandesc = {
+    optional => 1,
+    type => 'string', format => 'pve-qm-vlan',
+    typetext => "MODEL=XX:XX:XX:XX:XX:XX { , MODEL=YY:YY:YY:YY:YY:YY }",
+    description => <<EODESCR,
+Specify network devices connected to vlan[n]. Currently, vlan[n] is connected to vmbr[n] (n is 0 to 4094).
+
+MODEL is one of: $nic_model_list_txt
+
+XX:XX:XX:XX:XX:XX should be an unique MAC address
+EODESCR
+};
+
 for (my $i = 0; $i < $MAX_VLANS; $i++)  {
-    $confvars->{"vlan$i"} = 'vlan';
+    $confdesc->{"vlan$i"} = $vlandesc;
 }
 
 my $drivename_hash;
+    
+my $idedesc = {
+    optional => 1,
+    type => 'string', format => 'pve-qm-drive',
+    typetext => '[volume=]volume,] [,media=cdrom|disk] [,cyls=c,heads=h,secs=s[,trans=t]] [,snapshot=on|off] [,cache=none|writethrough|writeback] [,format=f]',
+    description => "Use volume as IDE hard disk or CD-ROM (n is 0 to 3).",
+};
 
+my $scsidesc = {
+    optional => 1,
+    type => 'string', format => 'pve-qm-drive',
+    typetext => '[volume=]volume,] [,media=cdrom|disk] [,cyls=c,heads=h,secs=s[,trans=t]] [,snapshot=on|off] [,cache=none|writethrough|writeback] [,format=f]',
+    description => "Use volume as SCSI hard disk or CD-ROM (n is 0 to 15).",
+};
+
+my $virtiodesc = {
+    optional => 1,
+    type => 'string', format => 'pve-qm-drive',
+    typetext => '[volume=]volume,] [,media=cdrom|disk] [,cyls=c,heads=h,secs=s[,trans=t]] [,snapshot=on|off] [,cache=none|writethrough|writeback] [,format=f]',
+    description => "Use volume as VIRTIO hard disk (n is 0 to 15).",
+};
+
 for (my $i = 0; $i < $MAX_IDE_DISKS; $i++)  {
-    $confvars->{"ide$i"} = 'drive';
     $drivename_hash->{"ide$i"} = 1;
+    $confdesc->{"ide$i"} = $idedesc;
 }
+
 for (my $i = 0; $i < $MAX_SCSI_DISKS; $i++)  {
-    $confvars->{"scsi$i"} = 'drive';
     $drivename_hash->{"scsi$i"} = 1;
+    $confdesc->{"scsi$i"} = $scsidesc ;
 }
 for (my $i = 0; $i < $MAX_VIRTIO_DISKS; $i++)  {
-    $confvars->{"virtio$i"} = 'drive';
     $drivename_hash->{"virtio$i"} = 1;
+    $confdesc->{"virtio$i"} = $virtiodesc;
 }
 
-my $defvars = {
-    onboot => ['bool', 0],
-    autostart => ['bool', 0],
-    keyboard => ['lang', undef],
-    cpuunits => ['natural', 1000],
-    memory => ['natural', 512],
-    vga => ['vgatype', 'cirrus'],
-    tdf => ['bool', 1],
-    tablet => ['bool', 1],
-    migrate_speed => ['natural', 0],
-    migrate_downtime => ['natural', 1],
-};
-
 my $kvm_api_version = 0;
 
 sub kvm_version {
@@ -258,8 +505,7 @@
 }
 
 sub nic_models {
-    return ['rtl8139', 'ne2k_pci', 'e1000',  'pcnet',  'virtio',
-	    'ne2k_isa', 'i82551', 'i82557b', 'i82559er'];
+    return $nic_model_list;
 }
 
 sub os_list_description {
@@ -401,6 +647,26 @@
     return $file;
 }
 
+sub verify_media_type {
+    my ($opt, $vtype, $media) = @_;
+
+    return if !$media;
+
+    my $etype;
+    if ($media eq 'disk') {
+	$etype = 'image';
+    } elsif ($media eq 'cdrom') {
+	$etype = 'iso';
+    } else {
+	die "internal error";
+    }
+
+    return if ($vtype eq $etype);
+    
+    raise_param_exc({ $opt => "unexpected media type ($vtype != $etype)" });
+}
+
+# fixme: remove
 sub parse_options {
     my ($self, $vmid, $create) = @_;
 
@@ -417,41 +683,39 @@
 	    }
 	}
 
-	my $ct = $confvars->{$opt};
+	if (!$create && ($value eq '' || $value eq 'undef')) {
+	    $settings->{$opt} = $value;
+	    return;
+	}
 
 	my $v;
-	if (defined ($v = check_type ($vmid, $ct, $opt, $value))) {
-	    my $drive = parse_drive ($opt, $value);
-	    if (($ct eq 'drive') &&
-		($drive->{file} !~ m/^(undef|delete|cdrom|none)$/) &&
-		($drive->{file} !~ m|^/dev/.+|) &&
-		($drive->{file} !~ m/^([^:]+):(.+)$/) &&
-		($drive->{file} !~ m/^\d+$/)) { 
-		# try to convert path to volume id
-		my $media = $drive->{media} || 'disk';
-		my ($vtype, $volid) = PVE::Storage::path_to_volume_id 
-		    ($self->{storecfg}, $drive->{file});
-		if (!$vtype) {
-		    die "unable to parse value for '$opt'\n";
-		} elsif ($vtype eq $media) {
+	eval { $v = check_type($opt, $value); };
+	if ($@) {
+	    die "unable to parse value for '$opt' : $@";
+	} else {
+	    my $fmt = $confdesc->{$opt}->{format};
+	    if ($fmt && $fmt eq 'pve-qm-drive') {
+		my $drive = parse_drive ($opt, $value);
+		if (($drive->{file} !~ m/^(undef|delete|cdrom|none)$/) &&
+		    ($drive->{file} !~ m|^/dev/.+|) &&
+		    ($drive->{file} !~ m/^([^:]+):(.+)$/) &&
+		    ($drive->{file} !~ m/^\d+$/)) { 
+		    # try to convert path to volume id
+		    my ($vtype, $volid) = PVE::Storage::path_to_volume_id 
+			($self->{storecfg}, $drive->{file});
+		    die "unable to parse value for '$opt'\n" if !$vtype;
+
+		    verify_media_type($opt, $vtype, $drive->{media});
 		    $drive->{file} = $volid;
 		    $v = print_drive ($vmid, $drive);
-		} else {
-		    die "unexpected media type for '$opt' ($vtype != $media)\n";
 		}
 	    }
 	    $settings->{$opt} = $v;
-	} else {
-	    if (!$create && ($value eq '' || $value eq 'undef')) {
-		$settings->{$opt} = $value;
-	    } else {
-		die "unable to parse value for '$opt'\n";
-	    }
 	}
     };
 
     my $getopt;
-    foreach my $opt (keys %$confvars) {
+    foreach my $opt (keys %$confdesc) {
 	$getopt->{"$opt=s"} = $get_option_func;
     }
 
@@ -464,6 +728,58 @@
     return $settings;
 }
 
+# fixme: should we return the parse value?
+sub parse_options_new {
+    my ($storecfg, $vmid, $param, $create) = @_;
+
+    my $settings = {};
+
+    foreach my $opt (keys %$param) {
+	my $value = $param->{$opt};
+
+	if (!defined($value)) {
+	    raise_param_exc({ $opt => "got undefined value" });
+	}
+	    
+	if ($opt eq 'cdrom') {
+	    $opt = 'ide2';
+	    if ($value && $value ne 'undef' && $value ne 'delete' && 
+		$value !~ m/media=/) {
+		$value .= ",media=cdrom"; 
+	    }
+	}
+
+	if (!$create && ($value eq '' || $value eq 'undef')) {
+	    $settings->{$opt} = $value;
+	    return;
+	}
+
+	my $v;
+	eval { $v = check_type($opt, $value); };
+	raise_param_exc({ $opt => $@ }) if $@;
+	my $fmt = $confdesc->{$opt}->{format};
+	if ($fmt && $fmt eq 'pve-qm-drive') {
+	    my $drive = parse_drive ($opt, $value);
+	    if (($drive->{file} !~ m/^(undef|delete|cdrom|none)$/) &&
+		($drive->{file} !~ m|^/dev/.+|) &&
+		($drive->{file} !~ m/^([^:]+):(.+)$/) &&
+		($drive->{file} !~ m/^\d+$/)) { 
+		# try to convert path to volume id
+		my ($vtype, $volid) = PVE::Storage::path_to_volume_id($storecfg, $drive->{file}) if $storecfg;
+
+	       
+		raise_param_exc({ $opt => "unable to associate path '$drive->{file}' to any storage"}) if !$vtype;
+		verify_media_type($opt, $vtype, $drive->{media});
+		$drive->{file} = $volid;
+		$v = print_drive ($vmid, $drive);
+	    }
+	}
+	$settings->{$opt} = $v;
+    }
+
+    return $settings;
+}
+
 sub create_conf_nolock {
     my ($self, $vmid, $settings) = @_;
 
@@ -482,7 +798,7 @@
 	die "unable to create config for VM $vmid - $!\n";
     
     foreach my $opt (keys %$settings) {
-	next if !$confvars->{$opt};
+	next if !$confdesc->{$opt};
 
 	my $value = $settings->{$opt};
 	next if !$value;
@@ -501,7 +817,11 @@
 
     my $res = {};
     
-    if ($key =~ m/^([^\d]+)(\d+)$/) {
+    # $key my be undefined - used to verify JSON parameters
+    if (!defined($key)) {
+	$res->{interface} = 'unknown'; # should not harm when used to verify parameters
+	$res->{index} = 0;
+    } elsif ($key =~ m/^([^\d]+)(\d+)$/) {
 	$res->{interface} = $1;
 	$res->{index} = $2;
     } else {
@@ -642,70 +962,157 @@
     return $res;
 }
 
+PVE::JSONSchema::register_format('pve-qm-bootdisk', \&verify_bootdisk);
+sub verify_bootdisk {
+    my ($value, $noerr) = @_;
+
+    foreach my $ds (disknames()) {
+	return $value if $value eq $ds;
+    }
+    return undef if $noerr;
+
+    die "invalid boot disk '$value'\n";
+}
+
+PVE::JSONSchema::register_format('pve-qm-vlan', \&verify_vlan);
+sub verify_vlan {
+    my ($value, $noerr) = @_;
+
+    return $value if parse_vlan ($value);
+
+    return undef if $noerr;
+    
+    die "unable to parse vlan\n";
+}
+
+PVE::JSONSchema::register_format('pve-qm-drive', \&verify_drive);
+sub verify_drive {
+    my ($value, $noerr) = @_;
+
+    return $value if parse_drive (undef, $value);
+
+    return undef if $noerr;
+    
+    die "unable to parse drive options\n";
+}
+
+PVE::JSONSchema::register_format('pve-qm-hostpci', \&verify_hostpci);
+sub verify_hostpci {
+    my ($value, $noerr) = @_;
+
+    my @dl = split (/,/, $value);
+    foreach my $v (@dl) {
+	if ($v !~ m/^[a-f0-9]{2}:[a-f0-9]{2}\.[a-f0-9]$/i) {
+	    return undef if $noerr;
+	    die "unable to parse pci id\n";
+	}
+    }
+    return $value;
+}
+
+PVE::JSONSchema::register_format('pve-qm-hostusb', \&verify_hostusb);
+sub verify_hostusb {
+    my ($value, $noerr) = @_;
+
+    my @dl = split (/,/, $value);
+    foreach my $v (@dl) {
+	if ($v !~ m/^(\d+.(\d+|\*)|[0-9A-Fa-f]+:([0-9A-Fa-f]+|\*))$/) {
+	    return undef if $noerr;
+	    die "unable to parse usb id\n";
+	}
+    }
+    return $value;
+}
+
+PVE::JSONSchema::register_format('pve-qm-parallel', \&verify_parallel);
+sub verify_parallel {
+    my ($value, $noerr) = @_;
+
+    my @dl = split (/,/, $value);
+    foreach my $v (@dl) {
+	if ($v !~ m|^/dev/parport\d+$|) {
+	    return undef if $noerr;
+	    die "invalid device name\n";
+	}
+    }
+    return $value;
+}
+
+PVE::JSONSchema::register_format('pve-qm-serial', \&verify_serial);
+sub verify_serial {
+    my ($value, $noerr) = @_;
+
+    my @dl = split (/,/, $value);
+    foreach my $v (@dl) {
+	if ($v !~ m|^/dev/ttyS\d+$|) {
+	    return undef if $noerr;
+	    die "invalid device name\n";
+	}
+    }
+    return $value;
+}
+
+# JSON properties for create and set function
+my $json_config_properties_cache;
+sub json_config_properties {
+    
+    return $json_config_properties_cache if $json_config_properties_cache;
+
+    my $prop = {
+	node => { type => 'string', format => 'pve-node' },
+	vmid => { 
+	    description => "The (unique) ID of the VM.",
+	    type => 'integer', format => 'pve-vmid',
+	    minimum => 1,
+	},
+    };
+
+    foreach my $opt (keys %$confdesc) {
+	$prop->{$opt} = $confdesc->{$opt};
+    }
+
+    $json_config_properties_cache = $prop;
+
+    return $prop;
+}
+
 sub check_type {
-    my ($vmid, $type, $key, $value) = @_;
+    my ($key, $value) = @_;
 
-    my $res;
+    die "unknown setting '$key'\n" if !$confdesc->{$key};
 
-    if ($type eq 'bool') {
+    my $type = $confdesc->{$key}->{type};
+
+    if (!defined ($value)) {
+	die "got undefined value\n";
+    }
+
+    if ($value =~ m/[\n\r]/) {
+	die "property contains a line feed\n";
+    }
+
+    if ($type eq 'boolean') {
 	return 1 if ($value eq '1') || ($value =~ m/^(on|yes|true)$/i); 
 	return 0 if ($value eq '0') || ($value =~ m/^(off|no|false)$/i); 
-    } elsif ($type eq 'natural') {
-	return $1 if $value =~ m/^(\d+)$/;
-    } elsif ($type eq 'lang') {
-	$value = lc ($value);
-	return $value if -f "/usr/share/kvm/keymaps/$value";
+	die "type check ('boolean') failed - got '$value'\n";	
+    } elsif ($type eq 'integer') {
+	return int($1) if $value =~ m/^(\d+)$/;
+	die "type check ('integer') failed - got '$value'\n";
     } elsif ($type eq 'string') {
+	if (my $fmt = $confdesc->{format}) {
+	    if ($fmt eq 'pve-qm-drive') {
+		my $drive = parse_drive ($key, $value);
+		return $value if $drive;
+		die "unable to parse drive options\n";
+	    }
+	    PVE::JSONSchema::check_format($fmt, $value);
+	    return $value;	
+	} 
 	$value =~ s/^\"(.*)\"$/$1/;
 	return $value;	
-    } elsif ($type eq 'ostype') {
-	return $value if $value =~ m/^(other|wxp|w2k|w2k3|w2k8|wvista|l24|l26)$/;
-    } elsif ($type eq 'vgatype') {
-	return $value if $value =~ m/^(std|cirrus)$/;
-    } elsif ($type eq 'boot') {
-	return $value if $value =~ m/^[acdn]{1,3}$/;
-    } elsif ($type eq 'bootdisk') {
-	foreach my $ds (disknames()) {
-	    return $value if $value eq $ds;
-	}
-	return undef;
-    } elsif ($type eq 'lockinfo') {
-	return $value if $value =~ m/^(migrate|backup)$/;	
-    } elsif ($type eq 'vlan') {
-	return $value if parse_vlan ($value);	
-    } elsif ($type eq 'drive') {
-	my $drive = parse_drive ($key, $value);
-	return $value if $drive;
-    } elsif ($type eq 'hostpci') {
-	my @dl = split (/,/, $value);
-	foreach my $v (@dl) {
-	    return undef if $v !~ m/^[a-f0-9]{2}:[a-f0-9]{2}\.[a-f0-9]$/i;
-	}
-	return $value;
-    } elsif ($type eq 'hostusb') {
-	my @dl = split (/,/, $value);
-	foreach my $v (@dl) {
-	    return undef if $v !~ m/^(\d+.(\d+|\*)|[0-9A-Fa-f]+:([0-9A-Fa-f]+|\*))$/
-	}
-	return $value;
-    } elsif ($type eq 'parallel') {
-	my @dl = split (/,/, $value);
-	foreach my $v (@dl) {
-	    return undef if $v !~ m|^/dev/parport\d+$|
-	}
-	return $value;	
-    } elsif ($type eq 'serial') {
-	my @dl = split (/,/, $value);
-	foreach my $v (@dl) {
-	    return undef if $v !~ m|^/dev/ttyS\d+$|
-	}
-	return $value;	
-    } elsif ($type eq 'startdate') {
-	return $value if $value eq 'now';
-	return $value if $value =~ m/^\d{4}-\d{1,2}-\d{1,2}(T\d{1,2}-\d{1,2}-\d{1,2})?$/;
+    } else {
+	die "internal error"
     }
-
-    return undef;
 }
 
 sub lock_config {
@@ -830,7 +1237,7 @@
 	PVE::Storage::foreach_volid ($dl, sub {
 	    my ($volid, $sid, $volname, $d) = @_;
 	    $info->{$volid} = $d;
-	});
+				     });
     };
     warn $@ if $@;
 
@@ -875,27 +1282,27 @@
 	} elsif ($line =~ m/^([a-z][a-z_]*\d*):\s*(\S+)\s*$/) {
 	    my $key = $1;
 	    my $value = $2;
-	    if (my $type = $confvars->{$key}) {
-		if (defined ($value = check_type ($vmid, $type, $key, $value))) {
-
-		    if ($type eq 'drive') {
-			my $v = parse_drive ($key, $value);
-			if (my $volid = filename_to_volume_id ($vmid, $v->{file}, $v->{media})) {
-			    $v->{file} = $volid;
-			    $value = print_drive ($vmid, $v);
-			} else {
-			    warn "vm $vmid - unable to parse value of '$key'\n";
-			    next;
-			}
+	    eval { $value = check_type($key, $value); };
+	    if ($@) {
+		warn "vm $vmid - unable to parse value of '$key' - $@";
+	    } else {
+		my $fmt = $confdesc->{$key}->{format};
+		if ($fmt && $fmt eq 'pve-qm-drive') {
+		    my $v = parse_drive ($key, $value);
+		    if (my $volid = filename_to_volume_id ($vmid, $v->{file}, $v->{media})) {
+			$v->{file} = $volid;
+			$value = print_drive ($vmid, $v);
+		    } else {
+			warn "vm $vmid - unable to parse value of '$key'\n";
+			next;
 		    }
+		}
 
+		if ($key eq 'cdrom') {
+		    $res->{ide2} = $value;
+		} else {
 		    $res->{$key} = $value;
-
-		} else {
-		    warn "vm $vmid - unable to parse value of '$key'\n";
 		}
-	    } else {
-		warn "vm $vmid - unknown setting '$key'\n";
 	    }
 	}
     }
@@ -937,6 +1344,8 @@
 
     my $res = {};
 
+    $unset->{ide2} = $unset->{cdrom} if $unset->{cdrom};
+
     check_lock ($settings) if !$skiplock;
 
     # we do not use 'smp' any longer
@@ -949,14 +1358,15 @@
 
     foreach my $key (keys %$settings) {
 	my $value = $settings->{$key};
-	if (my $type = $confvars->{$key}) {
-	    if (defined ($value = check_type ($vmid, $type, $key, $value))) {
+	eval { $value = check_type($key, $value); };
+	if ($@) {
+	    die "unable to parse value of '$key' - $@";
+	} else {
+	    if ($key eq 'cdrom') {
+		$res->{ide2} = $value;
+	    } else {
 		$res->{$key} = $value;
-	    } else {
-		die "unable to parse value of '$key'\n";
 	    }
-	} else {
-	    die "unknown setting '$key'\n";
 	}
     }
 
@@ -1041,9 +1451,10 @@
 
     my $res = {};
 
-    while (my ($key, $value) = each %$defvars) {
-	my ($type, $default) = @$value;
-	$res->{$key} = $default if defined ($default);
+    foreach my $key (keys %$confdesc) {
+	if (defined(my $default = $confdesc->{$key}->{default})) {
+	    $res->{$key} = $default;
+	}
     }
    
     my $fh = new IO::File ($filename, "r") || return $res;
@@ -1057,15 +1468,13 @@
 	if ($line =~ m/^([a-z][a-z_]*):\s*(\S+)\s*$/) {
 	    my $key = $1;
 	    my $value = $2;
-	    my ($type, $default);
 
-	    ($type, $default) = @{$defvars->{$key}} if defined $defvars->{$key};
-
-	    if ($type) {
-		if (defined ($value = check_type (undef, $type, $key, $value))) {
+	    if ($confdesc->{$key} && $confdesc->{$key}->{default}) {
+		eval { $value = check_type($key, $value); };
+		if ($@) {
+		    warn "load_defaults - unable to parse value of '$key' -  $@";
+		} else {
 		    $res->{$key} = $value;
-		} else {
-		    warn "load_defaults - unable to parse value of '$key'\n";
 		}
 	    } else {
 		warn "load_defaults - unknown setting '$key'\n";

Modified: qemu-server/pve2/nqm
===================================================================
--- qemu-server/pve2/nqm	2010-09-10 07:43:40 UTC (rev 5106)
+++ qemu-server/pve2/nqm	2010-09-10 07:54:17 UTC (rev 5107)
@@ -45,6 +45,9 @@
 
 		 
 	      } ],
+
+    create => [ "PVE::API2::QemuServer", 'create_vm', ['vmid'], { node => $hostname } ],
+		
 };
 
 my $cmd = shift;




More information about the pve-devel mailing list