Tomáš Golembiovský
2018-Feb-22 10:41 UTC
[Libguestfs] [PATCH v2 0/3] Make generated OVF more conforming to standard
The main reason for creating different OVF is that it can be used to create VM by oVirt REST API. The RHV export domain flavor cannot be used that way. v1 -> v2: - introduced flavour types instead of booleans - instead of referring to the new flavour as "standard OVF" or "fixed OVF" I refer to it as oVirt flavour. While it is more conforming than the one used in export domain it can still contain some oVirt specific pieces. Tomáš Golembiovský (3): v2v: tests: check generated OVF v2v: ovf: Create OVF more aligned with the standard v2v: vdsm: add --vdsm-fixed-ovf option v2v/Makefile.am | 2 + v2v/cmdline.ml | 10 ++ v2v/create_ovf.ml | 159 ++++++++++++++++++++++--------- v2v/create_ovf.mli | 21 +++- v2v/output_rhv.ml | 3 +- v2v/output_vdsm.ml | 10 +- v2v/output_vdsm.mli | 1 + v2v/test-v2v-o-rhv.ovf.expected | 92 ++++++++++++++++++ v2v/test-v2v-o-rhv.sh | 21 ++++ v2v/test-v2v-o-vdsm-options.ovf.expected | 92 ++++++++++++++++++ v2v/test-v2v-o-vdsm-options.sh | 20 +++- v2v/virt-v2v.pod | 20 ++++ 12 files changed, 400 insertions(+), 51 deletions(-) create mode 100644 v2v/test-v2v-o-rhv.ovf.expected create mode 100644 v2v/test-v2v-o-vdsm-options.ovf.expected -- 2.16.1
Tomáš Golembiovský
2018-Feb-22 10:41 UTC
[Libguestfs] [PATCH v2 1/3] v2v: tests: check generated OVF
Check the generated OVF for -o rhv and -o vdsm outputs. Variable UUIDs and date/times are filtered out. Make sure the the important UUIDs (disk, volume, VM) are where we think they should be. Signed-off-by: Tomáš Golembiovský <tgolembi@redhat.com> --- v2v/Makefile.am | 2 + v2v/test-v2v-o-rhv.ovf.expected | 92 ++++++++++++++++++++++++++++++++ v2v/test-v2v-o-rhv.sh | 21 ++++++++ v2v/test-v2v-o-vdsm-options.ovf.expected | 92 ++++++++++++++++++++++++++++++++ v2v/test-v2v-o-vdsm-options.sh | 17 ++++++ 5 files changed, 224 insertions(+) create mode 100644 v2v/test-v2v-o-rhv.ovf.expected create mode 100644 v2v/test-v2v-o-vdsm-options.ovf.expected diff --git a/v2v/Makefile.am b/v2v/Makefile.am index 3a978d727..8ec002fd3 100644 --- a/v2v/Makefile.am +++ b/v2v/Makefile.am @@ -472,7 +472,9 @@ EXTRA_DIST += \ test-v2v-o-libvirt.sh \ test-v2v-o-null.sh \ test-v2v-o-qemu.sh \ + test-v2v-o-rhv.ovf.expected \ test-v2v-o-rhv.sh \ + test-v2v-o-vdsm-options.ovf.expected \ test-v2v-o-vdsm-options.sh \ test-v2v-oa-option.sh \ test-v2v-of-option.sh \ diff --git a/v2v/test-v2v-o-rhv.ovf.expected b/v2v/test-v2v-o-rhv.ovf.expected new file mode 100644 index 000000000..f2f7839dc --- /dev/null +++ b/v2v/test-v2v-o-rhv.ovf.expected @@ -0,0 +1,92 @@ +<?xml version='1.0' encoding='utf-8'?> +<ovf:Envelope xmlns:rasd='http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData' xmlns:vssd='http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:ovf='http://schemas.dmtf.org/ovf/envelope/1/' ovf:version='0.9'> + <!-- generated by virt-v2v --> + <References> + <File ovf:href='#DISK_ID#/#VOL_ID#' ovf:id='#VOL_ID#' ovf:size='536870912' ovf:description='generated by virt-v2v'/> + </References> + <Section xsi:type='ovf:NetworkSection_Type'> + <Info>List of networks</Info> + <Network ovf:name='default'/> + </Section> + <Section xsi:type='ovf:DiskSection_Type'> + <Info>List of Virtual Disks</Info> + <Disk ovf:diskId='#VOL_ID#' ovf:size='1' ovf:fileRef='#DISK_ID#/#VOL_ID#' ovf:parentRef='' ovf:vm_snapshot_id='#UUID#' ovf:volume-format='RAW' ovf:volume-type='Sparse' ovf:format='http://en.wikipedia.org/wiki/Byte' ovf:disk-interface='IDE' ovf:disk-type='System' ovf:boot='True' ovf:actual_size='1'/> + </Section> + <Content ovf:id='out' xsi:type='ovf:VirtualSystem_Type'> + <Name>windows</Name> + <TemplateId>00000000-0000-0000-0000-000000000000</TemplateId> + <TemplateName>Blank</TemplateName> + <Description>generated by virt-v2v</Description> + <Domain/> + <CreationDate>#DATE#</CreationDate> + <IsInitilized>True</IsInitilized> + <IsAutoSuspend>False</IsAutoSuspend> + <TimeZone/> + <IsStateless>False</IsStateless> + <VmType>0</VmType> + <DefaultDisplayType>1</DefaultDisplayType> + <Section ovf:id='#VM_ID#' ovf:required='false' xsi:type='ovf:OperatingSystemSection_Type'> + <Info>Microsoft Windows 7 Phony Edition</Info> + <Description>Windows7</Description> + </Section> + <Section xsi:type='ovf:VirtualHardwareSection_Type'> + <Info>1 CPU, 1024 Memory</Info> + <Item> + <rasd:Caption>1 virtual cpu</rasd:Caption> + <rasd:Description>Number of virtual CPU</rasd:Description> + <rasd:InstanceId>1</rasd:InstanceId> + <rasd:ResourceType>3</rasd:ResourceType> + <rasd:num_of_sockets>1</rasd:num_of_sockets> + <rasd:cpu_per_socket>1</rasd:cpu_per_socket> + </Item> + <Item> + <rasd:Caption>1024 MB of memory</rasd:Caption> + <rasd:Description>Memory Size</rasd:Description> + <rasd:InstanceId>2</rasd:InstanceId> + <rasd:ResourceType>4</rasd:ResourceType> + <rasd:AllocationUnits>MegaBytes</rasd:AllocationUnits> + <rasd:VirtualQuantity>1024</rasd:VirtualQuantity> + </Item> + <Item> + <rasd:Caption>USB Controller</rasd:Caption> + <rasd:InstanceId>3</rasd:InstanceId> + <rasd:ResourceType>23</rasd:ResourceType> + <rasd:UsbPolicy>Disabled</rasd:UsbPolicy> + </Item> + <Item> + <rasd:Caption>Graphical Controller</rasd:Caption> + <rasd:InstanceId>#UUID#</rasd:InstanceId> + <rasd:ResourceType>20</rasd:ResourceType> + <Type>video</Type> + <rasd:VirtualQuantity>1</rasd:VirtualQuantity> + <rasd:Device>qxl</rasd:Device> + </Item> + <Item> + <rasd:Caption>Drive 1</rasd:Caption> + <rasd:InstanceId>#VOL_ID#</rasd:InstanceId> + <rasd:ResourceType>17</rasd:ResourceType> + <Type>disk</Type> + <rasd:HostResource>#DISK_ID#/#VOL_ID#</rasd:HostResource> + <rasd:Parent>00000000-0000-0000-0000-000000000000</rasd:Parent> + <rasd:Template>00000000-0000-0000-0000-000000000000</rasd:Template> + <rasd:ApplicationList/> + <rasd:StorageId>12345678-1234-1234-1234-123456789abc</rasd:StorageId> + <rasd:StoragePoolId>00000000-0000-0000-0000-000000000000</rasd:StoragePoolId> + <rasd:CreationDate>#DATE#</rasd:CreationDate> + <rasd:LastModified>#DATE#</rasd:LastModified> + <rasd:last_modified_date>#DATE#</rasd:last_modified_date> + <BootOrder>1</BootOrder> + </Item> + <Item> + <rasd:InstanceId>#UUID#</rasd:InstanceId> + <rasd:Caption>Ethernet adapter on default</rasd:Caption> + <rasd:ResourceType>10</rasd:ResourceType> + <rasd:ResourceSubType>1</rasd:ResourceSubType> + <Type>interface</Type> + <rasd:Connection>default</rasd:Connection> + <rasd:Name>eth0</rasd:Name> + <rasd:MACAddress>00:11:22:33:44:55</rasd:MACAddress> + </Item> + </Section> + </Content> +</ovf:Envelope> diff --git a/v2v/test-v2v-o-rhv.sh b/v2v/test-v2v-o-rhv.sh index 413e01348..d653caa1a 100755 --- a/v2v/test-v2v-o-rhv.sh +++ b/v2v/test-v2v-o-rhv.sh @@ -60,4 +60,25 @@ test -f $vol popd +# Compare resulting OVF +VM_ID=$(basename $(ls -1d $d/12345678-1234-1234-1234-123456789abc/master/vms/*)) +DISK_ID=$(basename $(ls -1d $d/12345678-1234-1234-1234-123456789abc/images/*)) +VOL_ID=$(basename $(ls -1d $d/12345678-1234-1234-1234-123456789abc/images/$DISK_ID/*.meta) .meta) +OVF=$(ls -1d $d/12345678-1234-1234-1234-123456789abc/master/vms/$VM_ID/$VM_ID.ovf) + +RE_UUID='\<[0-9a-fA-F]\{8\}-[0-9a-fA-F]\{4\}-[0-9a-fA-F]\{4\}-[0-9a-fA-F]\{4\}-[0-9a-fA-F]\{12\}\>' + +# Filter variable strings +sed -i \ + -e "s/$DISK_ID/#DISK_ID#/g" \ + -e "s/$VM_ID/#VM_ID#/g" \ + -e "s/$VOL_ID/#VOL_ID#/g" \ + -e 's/\(\<generated by virt-v2v\) [^ <'"'\""']*/\1/' \ + -e 's/<rasd:InstanceId>'"$RE_UUID"'</<rasd:InstanceId>#UUID#</' \ + -e 's/<\(rasd:\)\?\(CreationDate\|LastModified\|last_modified_date\)>[^<]*</<\1\2>#DATE#</' \ + -e 's/\<ovf:vm_snapshot_id='"'$RE_UUID'/ovf:vm_snapshot_id='#UUID#'/g" \ + "$OVF" + +diff -u test-v2v-o-rhv.ovf.expected "$OVF" + rm -r $d diff --git a/v2v/test-v2v-o-vdsm-options.ovf.expected b/v2v/test-v2v-o-vdsm-options.ovf.expected new file mode 100644 index 000000000..372469e21 --- /dev/null +++ b/v2v/test-v2v-o-vdsm-options.ovf.expected @@ -0,0 +1,92 @@ +<?xml version='1.0' encoding='utf-8'?> +<ovf:Envelope xmlns:rasd='http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData' xmlns:vssd='http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:ovf='http://schemas.dmtf.org/ovf/envelope/1/' ovf:version='0.9'> + <!-- generated by virt-v2v --> + <References> + <File ovf:href='IMAGE/VOL' ovf:id='VOL' ovf:size='536870912' ovf:description='generated by virt-v2v'/> + </References> + <Section xsi:type='ovf:NetworkSection_Type'> + <Info>List of networks</Info> + <Network ovf:name='default'/> + </Section> + <Section xsi:type='ovf:DiskSection_Type'> + <Info>List of Virtual Disks</Info> + <Disk ovf:diskId='VOL' ovf:size='1' ovf:fileRef='IMAGE/VOL' ovf:parentRef='' ovf:vm_snapshot_id='#UUID#' ovf:volume-format='COW' ovf:volume-type='Sparse' ovf:format='http://en.wikipedia.org/wiki/Byte' ovf:disk-interface='IDE' ovf:disk-type='System' ovf:boot='True' ovf:actual_size='1'/> + </Section> + <Content ovf:id='out' xsi:type='ovf:VirtualSystem_Type'> + <Name>windows</Name> + <TemplateId>00000000-0000-0000-0000-000000000000</TemplateId> + <TemplateName>Blank</TemplateName> + <Description>generated by virt-v2v</Description> + <Domain/> + <CreationDate>#DATE#</CreationDate> + <IsInitilized>True</IsInitilized> + <IsAutoSuspend>False</IsAutoSuspend> + <TimeZone/> + <IsStateless>False</IsStateless> + <VmType>0</VmType> + <DefaultDisplayType>1</DefaultDisplayType> + <Section ovf:id='VM' ovf:required='false' xsi:type='ovf:OperatingSystemSection_Type'> + <Info>Microsoft Windows 7 Phony Edition</Info> + <Description>Windows7</Description> + </Section> + <Section xsi:type='ovf:VirtualHardwareSection_Type'> + <Info>1 CPU, 1024 Memory</Info> + <Item> + <rasd:Caption>1 virtual cpu</rasd:Caption> + <rasd:Description>Number of virtual CPU</rasd:Description> + <rasd:InstanceId>1</rasd:InstanceId> + <rasd:ResourceType>3</rasd:ResourceType> + <rasd:num_of_sockets>1</rasd:num_of_sockets> + <rasd:cpu_per_socket>1</rasd:cpu_per_socket> + </Item> + <Item> + <rasd:Caption>1024 MB of memory</rasd:Caption> + <rasd:Description>Memory Size</rasd:Description> + <rasd:InstanceId>2</rasd:InstanceId> + <rasd:ResourceType>4</rasd:ResourceType> + <rasd:AllocationUnits>MegaBytes</rasd:AllocationUnits> + <rasd:VirtualQuantity>1024</rasd:VirtualQuantity> + </Item> + <Item> + <rasd:Caption>USB Controller</rasd:Caption> + <rasd:InstanceId>3</rasd:InstanceId> + <rasd:ResourceType>23</rasd:ResourceType> + <rasd:UsbPolicy>Disabled</rasd:UsbPolicy> + </Item> + <Item> + <rasd:Caption>Graphical Controller</rasd:Caption> + <rasd:InstanceId>#UUID#</rasd:InstanceId> + <rasd:ResourceType>20</rasd:ResourceType> + <Type>video</Type> + <rasd:VirtualQuantity>1</rasd:VirtualQuantity> + <rasd:Device>qxl</rasd:Device> + </Item> + <Item> + <rasd:Caption>Drive 1</rasd:Caption> + <rasd:InstanceId>VOL</rasd:InstanceId> + <rasd:ResourceType>17</rasd:ResourceType> + <Type>disk</Type> + <rasd:HostResource>IMAGE/VOL</rasd:HostResource> + <rasd:Parent>00000000-0000-0000-0000-000000000000</rasd:Parent> + <rasd:Template>00000000-0000-0000-0000-000000000000</rasd:Template> + <rasd:ApplicationList/> + <rasd:StorageId>12345678-1234-1234-1234-123456789abc</rasd:StorageId> + <rasd:StoragePoolId>00000000-0000-0000-0000-000000000000</rasd:StoragePoolId> + <rasd:CreationDate>#DATE#</rasd:CreationDate> + <rasd:LastModified>#DATE#</rasd:LastModified> + <rasd:last_modified_date>#DATE#</rasd:last_modified_date> + <BootOrder>1</BootOrder> + </Item> + <Item> + <rasd:InstanceId>#UUID#</rasd:InstanceId> + <rasd:Caption>Ethernet adapter on default</rasd:Caption> + <rasd:ResourceType>10</rasd:ResourceType> + <rasd:ResourceSubType>1</rasd:ResourceSubType> + <Type>interface</Type> + <rasd:Connection>default</rasd:Connection> + <rasd:Name>eth0</rasd:Name> + <rasd:MACAddress>00:11:22:33:44:55</rasd:MACAddress> + </Item> + </Section> + </Content> +</ovf:Envelope> diff --git a/v2v/test-v2v-o-vdsm-options.sh b/v2v/test-v2v-o-vdsm-options.sh index 1da252f17..44ad79a36 100755 --- a/v2v/test-v2v-o-vdsm-options.sh +++ b/v2v/test-v2v-o-vdsm-options.sh @@ -73,4 +73,21 @@ qemu-img info VOL | grep 'compat: 1.1' popd +# Compare resulting OVF +OVF="$d/12345678-1234-1234-1234-123456789abc/master/vms/VM/VM.ovf" + +RE_UUID='\<[0-9a-fA-F]\{8\}-[0-9a-fA-F]\{4\}-[0-9a-fA-F]\{4\}-[0-9a-fA-F]\{4\}-[0-9a-fA-F]\{12\}\>' + +# Filter variable strings +sed -i \ + -e 's/\(\<generated by virt-v2v\) [^ <'"'\""']*/\1/' \ + -e 's/<rasd:InstanceId>'"$RE_UUID"'</<rasd:InstanceId>#UUID#</' \ + -e 's/<\(rasd:\)\?\(CreationDate\|LastModified\|last_modified_date\)>[^<]*</<\1\2>#DATE#</' \ + -e 's/\<ovf:vm_snapshot_id='"'$RE_UUID'/ovf:vm_snapshot_id='#UUID#'/g" \ + "$OVF" + + +diff -u test-v2v-o-vdsm-options.ovf.expected \ + "$OVF" + rm -r $d -- 2.16.1
Tomáš Golembiovský
2018-Feb-22 10:41 UTC
[Libguestfs] [PATCH v2 2/3] v2v: ovf: Create OVF more aligned with the standard
For historical reasons the OVF used in RHV export domain contains some deviations from the OVF standard. The format used in -o rhv has to remain fixed but for -o vdsm and we could produce much nicer OVF. This patch serves as a preparatory step to this. The main reason for creating different OVF is that it can be used to create VM by oVirt REST API. The RHV export domain flavor cannot be used that way. For now the virt-v2v behavior is unchanged. The modified output will be enabled in some later patch. Signed-off-by: Tomáš Golembiovský <tgolembi@redhat.com> --- v2v/create_ovf.ml | 152 +++++++++++++++++++++++++++++++++++++---------------- v2v/create_ovf.mli | 12 +++-- v2v/output_rhv.ml | 3 +- v2v/output_vdsm.ml | 3 +- 4 files changed, 121 insertions(+), 49 deletions(-) diff --git a/v2v/create_ovf.ml b/v2v/create_ovf.ml index f60ca9ed3..8eeaa0945 100644 --- a/v2v/create_ovf.ml +++ b/v2v/create_ovf.ml @@ -29,6 +29,10 @@ open Types open Utils open DOM +type ovf_flavour + | OVirt + | RHVExportStorageDomain + (* We set the creation time to be the same for all dates in * all metadata files. All dates in OVF are UTC. *) @@ -269,7 +273,7 @@ let create_meta_files output_alloc sd_uuid image_uuids targets (* Create the OVF file. *) let rec create_ovf source targets guestcaps inspect - output_alloc sd_uuid image_uuids vol_uuids vm_uuid + output_alloc sd_uuid image_uuids vol_uuids vm_uuid ovf_flavour assert (List.length targets = List.length vol_uuids); let memsize_mb = source.s_memory /^ 1024L /^ 1024L in @@ -288,12 +292,26 @@ let rec create_ovf source targets guestcaps inspect ] [ Comment generated_by; e "References" [] []; - e "Section" ["xsi:type", "ovf:NetworkSection_Type"] [ - e "Info" [] [PCData "List of networks"] - ]; - e "Section" ["xsi:type", "ovf:DiskSection_Type"] [ - e "Info" [] [PCData "List of Virtual Disks"] - ]; + (match ovf_flavour with + | OVirt -> + e "NetworkSection" [] [ + e "Info" [] [PCData "List of networks"] + ] + | RHVExportStorageDomain -> + e "Section" ["xsi:type", "ovf:NetworkSection_Type"] [ + e "Info" [] [PCData "List of networks"] + ] + ); + (match ovf_flavour with + | OVirt -> + e "DiskSection" [] [ + e "Info" [] [PCData "List of Virtual Disks"] + ] + | RHVExportStorageDomain -> + e "Section" ["xsi:type", "ovf:DiskSection_Type"] [ + e "Info" [] [PCData "List of Virtual Disks"] + ] + ); let content_subnodes = ref [ e "Name" [] [PCData source.s_name]; @@ -326,11 +344,20 @@ let rec create_ovf source targets guestcaps inspect ); List.push_back content_subnodes ( - e "Section" ["ovf:id", vm_uuid; "ovf:required", "false"; - "xsi:type", "ovf:OperatingSystemSection_Type"] [ + let osinfo_subnodes = [ e "Info" [] [PCData inspect.i_product_name]; e "Description" [] [PCData ostype]; - ] + ] in + (match ovf_flavour with + | OVirt -> + e "OperatingSystemSection" ["ovf:id", vm_uuid; + "ovf:required", "false"] + osinfo_subnodes + | RHVExportStorageDomain -> + e "Section" ["ovf:id", vm_uuid; "ovf:required", "false"; + "xsi:type", "ovf:OperatingSystemSection_Type"] + osinfo_subnodes + ) ); let virtual_hardware_section_items = ref [ @@ -424,24 +451,34 @@ let rec create_ovf source targets guestcaps inspect ); List.push_back content_subnodes ( - e "Section" ["xsi:type", "ovf:VirtualHardwareSection_Type"] - !virtual_hardware_section_items + match ovf_flavour with + | OVirt -> + e "VirtualHardwareSection" [] !virtual_hardware_section_items + | RHVExportStorageDomain -> + e "Section" ["xsi:type", "ovf:VirtualHardwareSection_Type"] + !virtual_hardware_section_items ); - e "Content" ["ovf:id", "out"; "xsi:type", "ovf:VirtualSystem_Type"] - !content_subnodes + (match ovf_flavour with + | OVirt -> + e "VirtualSystem" ["ovf:id", "out"] !content_subnodes + | RHVExportStorageDomain -> + e "Content" ["ovf:id", "out"; "xsi:type", "ovf:VirtualSystem_Type"] + !content_subnodes + ) ] in (* Add disks to the OVF XML. *) - add_disks targets guestcaps output_alloc sd_uuid image_uuids vol_uuids ovf; + add_disks targets guestcaps output_alloc sd_uuid image_uuids vol_uuids + ovf_flavour ovf; (* Old virt-v2v ignored removable media. XXX *) (* Add networks to the OVF XML. *) - add_networks source.s_nics guestcaps ovf; + add_networks source.s_nics guestcaps ovf_flavour ovf; (* Add sound card to the OVF XML. *) - add_sound_card source.s_sound ovf; + add_sound_card source.s_sound ovf_flavour ovf; (* Old virt-v2v didn't really look at the video and display * metadata, instead just adding a single standard display (see @@ -461,21 +498,42 @@ let rec create_ovf source targets guestcaps inspect (* Return the OVF document. *) ovf +(* Find appropriate section depending on the OVF flavour being generated. + * + * For example normal disk section is in node <DiskSection> whereas in case of + * RHV export storage domain it is <Section xsi:type="ovf:DiskSection_Type">. + *) +and get_flavoured_section ovf ovf_flavour ovirt_path rhv_path rhv_path_attr + let nodes + match ovf_flavour with + | OVirt -> + let nodes = path_to_nodes ovf ovirt_path in + (match nodes with + | [node] -> node + | [] | _::_::_ -> assert false) + | RHVExportStorageDomain -> + let nodes = path_to_nodes ovf rhv_path in + try find_node_by_attr nodes rhv_path_attr + with Not_found -> assert false + in + nodes + (* This modifies the OVF DOM, adding a section for each disk. *) -and add_disks targets guestcaps output_alloc sd_uuid image_uuids vol_uuids ovf +and add_disks targets guestcaps output_alloc sd_uuid image_uuids vol_uuids + ovf_flavour ovf let references let nodes = path_to_nodes ovf ["ovf:Envelope"; "References"] in match nodes with | [] | _::_::_ -> assert false | [node] -> node in - let disk_section - let sections = path_to_nodes ovf ["ovf:Envelope"; "Section"] in - try find_node_by_attr sections ("xsi:type", "ovf:DiskSection_Type") - with Not_found -> assert false in - let virtualhardware_section - let sections = path_to_nodes ovf ["ovf:Envelope"; "Content"; "Section"] in - try find_node_by_attr sections ("xsi:type", "ovf:VirtualHardwareSection_Type") - with Not_found -> assert false in + let disk_section = get_flavoured_section ovf ovf_flavour + ["ovf:Envelope"; "DiskSection"] + ["ovf:Envelope"; "Section"] + ("xsi:type", "ovf:DiskSection_Type") in + let virtualhardware_section = get_flavoured_section ovf ovf_flavour + ["ovf:Envelope"; "VirtualSystem"; "VirtualHardwareSection"] + ["ovf:Envelope"; "Content"; "Section"] + ("xsi:type", "ovf:VirtualHardwareSection_Type") in (* Iterate over the disks, adding them to the OVF document. *) List.iteri ( @@ -489,7 +547,12 @@ and add_disks targets guestcaps output_alloc sd_uuid image_uuids vol_uuids ovf let is_bootable_drive = i == 0 in let boot_order = i+1 in - let fileref = sprintf "%s/%s" image_uuid vol_uuid in + let fileref + match ovf_flavour with + | OVirt -> + vol_uuid + | RHVExportStorageDomain -> + sprintf "%s/%s" image_uuid vol_uuid in (* ovf:size and ovf:actual_size fields are integer GBs. If you * use floating point numbers then RHV will fail to parse them. @@ -535,7 +598,10 @@ and add_disks targets guestcaps output_alloc sd_uuid image_uuids vol_uuids ovf (* Add disk to DiskSection. *) let disk let attrs = ref [ - "ovf:diskId", vol_uuid; + "ovf:diskId", + (match ovf_flavour with + | OVirt -> image_uuid + | RHVExportStorageDomain -> vol_uuid); "ovf:size", Int64.to_string size_gb; "ovf:fileRef", fileref; "ovf:parentRef", ""; @@ -593,15 +659,15 @@ and add_disks targets guestcaps output_alloc sd_uuid image_uuids vol_uuids ovf ) (List.combine3 targets image_uuids vol_uuids) (* This modifies the OVF DOM, adding a section for each NIC. *) -and add_networks nics guestcaps ovf - let network_section - let sections = path_to_nodes ovf ["ovf:Envelope"; "Section"] in - try find_node_by_attr sections ("xsi:type", "ovf:NetworkSection_Type") - with Not_found -> assert false in - let virtualhardware_section - let sections = path_to_nodes ovf ["ovf:Envelope"; "Content"; "Section"] in - try find_node_by_attr sections ("xsi:type", "ovf:VirtualHardwareSection_Type") - with Not_found -> assert false in +and add_networks nics guestcaps ovf_flavour ovf + let network_section = get_flavoured_section ovf ovf_flavour + ["ovf:Envelope"; "NetworkSection"] + ["ovf:Envelope"; "Section"] + ("xsi:type", "ovf:NetworkSection_Type") in + let virtualhardware_section = get_flavoured_section ovf ovf_flavour + ["ovf:Envelope"; "VirtualSystem"; "VirtualHardwareSection"] + ["ovf:Envelope"; "Content"; "Section"] + ("xsi:type", "ovf:VirtualHardwareSection_Type") in (* Iterate over the NICs, adding them to the OVF document. *) List.iteri ( @@ -649,7 +715,7 @@ and add_networks nics guestcaps ovf ) nics (* This modifies the OVF DOM, adding a sound card, if oVirt can emulate it. *) -and add_sound_card sound ovf +and add_sound_card sound ovf_flavour ovf let device match sound with | None -> None @@ -662,12 +728,10 @@ and add_sound_card sound ovf match device with | Some device -> - let virtualhardware_section - let sections - path_to_nodes ovf ["ovf:Envelope"; "Content"; "Section"] in - try find_node_by_attr sections - ("xsi:type", "ovf:VirtualHardwareSection_Type") - with Not_found -> assert false in + let virtualhardware_section = get_flavoured_section ovf ovf_flavour + ["ovf:Envelope"; "VirtualSystem"; "VirtualHardwareSection"] + ["ovf:Envelope"; "Content"; "Section"] + ("xsi:type", "ovf:VirtualHardwareSection_Type") in let item e "Item" [] [ diff --git a/v2v/create_ovf.mli b/v2v/create_ovf.mli index 07e8af6a0..8a8c7dd12 100644 --- a/v2v/create_ovf.mli +++ b/v2v/create_ovf.mli @@ -16,16 +16,22 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) +type ovf_flavour + | OVirt + | RHVExportStorageDomain + (** Create OVF and related files for RHV. - The format is described in - http://www.ovirt.org/images/8/86/Ovirt_ovf_format.odt + The format for RHV export storage domain is described in: + http://resources.ovirt.org/old-site-files/Ovirt_ovf_format.odt + + The format understood by oVirt has no known documentation. OVF isn't a real standard, so it's likely that if we ever had to create OVF for another target management system then we would need to heavily modify or even duplicate this code. *) -val create_ovf : Types.source -> Types.target list -> Types.guestcaps -> Types.inspect -> Types.output_allocation -> string -> string list -> string list -> string -> DOM.doc +val create_ovf : Types.source -> Types.target list -> Types.guestcaps -> Types.inspect -> Types.output_allocation -> string -> string list -> string list -> string -> ovf_flavour -> DOM.doc (** Create the OVF file. Actually a {!DOM} document is created, not a file. It can be written diff --git a/v2v/output_rhv.ml b/v2v/output_rhv.ml index 0b732e4cf..5260ab030 100644 --- a/v2v/output_rhv.ml +++ b/v2v/output_rhv.ml @@ -275,7 +275,8 @@ object (* Create the metadata. *) let ovf = Create_ovf.create_ovf source targets guestcaps inspect - output_alloc esd_uuid image_uuids vol_uuids vm_uuid in + output_alloc esd_uuid image_uuids vol_uuids vm_uuid + Create_ovf.RHVExportStorageDomain in (* Write it to the metadata file. *) let dir = esd_mp // esd_uuid // "master" // "vms" // vm_uuid in diff --git a/v2v/output_vdsm.ml b/v2v/output_vdsm.ml index 828ad7bda..76a3a0bd3 100644 --- a/v2v/output_vdsm.ml +++ b/v2v/output_vdsm.ml @@ -175,7 +175,8 @@ object output_alloc dd_uuid vdsm_params.image_uuids vdsm_params.vol_uuids - vdsm_params.vm_uuid in + vdsm_params.vm_uuid + Create_ovf.RHVExportStorageDomain in (* Write it to the metadata file. *) let file = vdsm_params.ovf_output // vdsm_params.vm_uuid ^ ".ovf" in -- 2.16.1
Tomáš Golembiovský
2018-Feb-22 10:41 UTC
[Libguestfs] [PATCH v2 3/3] v2v: vdsm: add --vdsm-fixed-ovf option
Add option for -o vdsm that enables output of the modified OVF. oVirt engine should already be able to consume the OVF, but let's not take any chances and enable it only by command line argument. It can be made default later when it receives proper testing. Signed-off-by: Tomáš Golembiovský <tgolembi@redhat.com> --- v2v/cmdline.ml | 10 ++++++++++ v2v/create_ovf.ml | 7 +++++++ v2v/create_ovf.mli | 9 +++++++++ v2v/output_vdsm.ml | 9 +++++++-- v2v/output_vdsm.mli | 1 + v2v/test-v2v-o-vdsm-options.ovf.expected | 26 +++++++++++++------------- v2v/test-v2v-o-vdsm-options.sh | 3 ++- v2v/virt-v2v.pod | 20 ++++++++++++++++++++ 8 files changed, 69 insertions(+), 16 deletions(-) diff --git a/v2v/cmdline.ml b/v2v/cmdline.ml index efad080cc..8d7c5e217 100644 --- a/v2v/cmdline.ml +++ b/v2v/cmdline.ml @@ -79,6 +79,11 @@ let parse_cmdline () let vdsm_compat = ref "0.10" in let set_vdsm_compat s = vdsm_compat := s in + let vdsm_ovf_flavour = ref Create_ovf.RHVExportStorageDomain in + let ovf_flavours_str = String.concat "|" Create_ovf.ovf_flavours in + let set_vdsm_ovf_flavour arg + vdsm_ovf_flavour := Create_ovf.ovf_flavour_of_string arg in + let set_string_option_once optname optref arg match !optref with | Some _ -> @@ -251,6 +256,8 @@ let parse_cmdline () s_"Output VM UUID"; [ L"vdsm-ovf-output" ], Getopt.String ("-", set_string_option_once "--vdsm-ovf-output" vdsm_ovf_output), s_"Output OVF file"; + [ L"vdsm-ovf-flavour" ], Getopt.Symbol (ovf_flavours_str, Create_ovf.ovf_flavours, set_vdsm_ovf_flavour), + s_"Set the type of generated OVF (default rhvexp)"; [ L"vmtype" ], Getopt.String ("-", vmtype_warning), s_"Ignored for backwards compatibility"; ] in @@ -327,6 +334,7 @@ read the man page virt-v2v(1). let vdsm_vol_uuids = List.rev !vdsm_vol_uuids in let vdsm_vm_uuid = !vdsm_vm_uuid in let vdsm_ovf_output = Option.default "." !vdsm_ovf_output in + let vdsm_ovf_flavour = !vdsm_ovf_flavour in (* No arguments and machine-readable mode? Print out some facts * about what this binary supports. @@ -343,6 +351,7 @@ read the man page virt-v2v(1). List.iter (printf "input:%s\n") (Modules_list.input_modules ()); List.iter (printf "output:%s\n") (Modules_list.output_modules ()); List.iter (printf "convert:%s\n") (Modules_list.convert_modules ()); + List.iter (printf "ovf:%s\n") Create_ovf.ovf_flavours; exit 0 ); @@ -542,6 +551,7 @@ read the man page virt-v2v(1). vm_uuid = vdsm_vm_uuid; ovf_output = vdsm_ovf_output; compat = vdsm_compat; + ovf_flavour = vdsm_ovf_flavour; } in Output_vdsm.output_vdsm os vdsm_params output_alloc, output_format, output_alloc in diff --git a/v2v/create_ovf.ml b/v2v/create_ovf.ml index 8eeaa0945..5d5682b1a 100644 --- a/v2v/create_ovf.ml +++ b/v2v/create_ovf.ml @@ -33,6 +33,13 @@ type ovf_flavour | OVirt | RHVExportStorageDomain +let ovf_flavours = ["ovirt"; "rhvexp"] + +let ovf_flavour_of_string = function + | "ovirt" -> OVirt + | "rhvexp" -> RHVExportStorageDomain + | flav -> invalid_arg flav + (* We set the creation time to be the same for all dates in * all metadata files. All dates in OVF are UTC. *) diff --git a/v2v/create_ovf.mli b/v2v/create_ovf.mli index 8a8c7dd12..2d80660e3 100644 --- a/v2v/create_ovf.mli +++ b/v2v/create_ovf.mli @@ -20,6 +20,15 @@ type ovf_flavour | OVirt | RHVExportStorageDomain +(** The string representation of available OVF flavours. *) +val ovf_flavours : string list + +(** Convert from a string to the corresponding OVF flavour. + + Throw [Invalid_argument] if the string does not match any + valid flavour. *) +val ovf_flavour_of_string : string -> ovf_flavour + (** Create OVF and related files for RHV. The format for RHV export storage domain is described in: diff --git a/v2v/output_vdsm.ml b/v2v/output_vdsm.ml index 76a3a0bd3..32615fa1f 100644 --- a/v2v/output_vdsm.ml +++ b/v2v/output_vdsm.ml @@ -32,6 +32,7 @@ type vdsm_params = { vm_uuid : string; ovf_output : string; compat : string; + ovf_flavour : Create_ovf.ovf_flavour; } class output_vdsm os vdsm_params output_alloc @@ -39,7 +40,7 @@ object inherit output method as_options - sprintf "-o vdsm -os %s%s%s --vdsm-vm-uuid %s --vdsm-ovf-output %s%s" os + sprintf "-o vdsm -os %s%s%s --vdsm-vm-uuid %s --vdsm-ovf-output %s%s%s" os (String.concat "" (List.map (sprintf " --vdsm-image-uuid %s") vdsm_params.image_uuids)) (String.concat "" @@ -49,6 +50,10 @@ object (match vdsm_params.compat with | "0.10" -> "" (* currently this is the default, so don't print it *) | s -> sprintf " --vdsm-compat=%s" s) + (match vdsm_params.ovf_flavour with + | Create_ovf.OVirt -> "--vdsm-ovf-flavour=ovf" + (* currently this is the default, so don't print it *) + | Create_ovf.RHVExportStorageDomain -> "") method supported_firmware = [ TargetBIOS ] @@ -176,7 +181,7 @@ object vdsm_params.image_uuids vdsm_params.vol_uuids vdsm_params.vm_uuid - Create_ovf.RHVExportStorageDomain in + vdsm_params.ovf_flavour in (* Write it to the metadata file. *) let file = vdsm_params.ovf_output // vdsm_params.vm_uuid ^ ".ovf" in diff --git a/v2v/output_vdsm.mli b/v2v/output_vdsm.mli index 95a84ee46..f7f6f59ee 100644 --- a/v2v/output_vdsm.mli +++ b/v2v/output_vdsm.mli @@ -24,6 +24,7 @@ type vdsm_params = { vm_uuid : string; (* --vdsm-vm-uuid *) ovf_output : string; (* --vdsm-ovf-output *) compat : string; (* --vdsm-compat=0.10|1.1 *) + ovf_flavour : Create_ovf.ovf_flavour; } (** Miscellaneous extra command line parameters used by VDSM. *) diff --git a/v2v/test-v2v-o-vdsm-options.ovf.expected b/v2v/test-v2v-o-vdsm-options.ovf.expected index 372469e21..babf88b7d 100644 --- a/v2v/test-v2v-o-vdsm-options.ovf.expected +++ b/v2v/test-v2v-o-vdsm-options.ovf.expected @@ -2,17 +2,17 @@ <ovf:Envelope xmlns:rasd='http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData' xmlns:vssd='http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:ovf='http://schemas.dmtf.org/ovf/envelope/1/' ovf:version='0.9'> <!-- generated by virt-v2v --> <References> - <File ovf:href='IMAGE/VOL' ovf:id='VOL' ovf:size='536870912' ovf:description='generated by virt-v2v'/> + <File ovf:href='VOL' ovf:id='VOL' ovf:size='536870912' ovf:description='generated by virt-v2v'/> </References> - <Section xsi:type='ovf:NetworkSection_Type'> + <NetworkSection> <Info>List of networks</Info> <Network ovf:name='default'/> - </Section> - <Section xsi:type='ovf:DiskSection_Type'> + </NetworkSection> + <DiskSection> <Info>List of Virtual Disks</Info> - <Disk ovf:diskId='VOL' ovf:size='1' ovf:fileRef='IMAGE/VOL' ovf:parentRef='' ovf:vm_snapshot_id='#UUID#' ovf:volume-format='COW' ovf:volume-type='Sparse' ovf:format='http://en.wikipedia.org/wiki/Byte' ovf:disk-interface='IDE' ovf:disk-type='System' ovf:boot='True' ovf:actual_size='1'/> - </Section> - <Content ovf:id='out' xsi:type='ovf:VirtualSystem_Type'> + <Disk ovf:diskId='IMAGE' ovf:size='1' ovf:fileRef='VOL' ovf:parentRef='' ovf:vm_snapshot_id='#UUID#' ovf:volume-format='COW' ovf:volume-type='Sparse' ovf:format='http://en.wikipedia.org/wiki/Byte' ovf:disk-interface='IDE' ovf:disk-type='System' ovf:boot='True' ovf:actual_size='1'/> + </DiskSection> + <VirtualSystem ovf:id='out'> <Name>windows</Name> <TemplateId>00000000-0000-0000-0000-000000000000</TemplateId> <TemplateName>Blank</TemplateName> @@ -25,11 +25,11 @@ <IsStateless>False</IsStateless> <VmType>0</VmType> <DefaultDisplayType>1</DefaultDisplayType> - <Section ovf:id='VM' ovf:required='false' xsi:type='ovf:OperatingSystemSection_Type'> + <OperatingSystemSection ovf:id='VM' ovf:required='false'> <Info>Microsoft Windows 7 Phony Edition</Info> <Description>Windows7</Description> - </Section> - <Section xsi:type='ovf:VirtualHardwareSection_Type'> + </OperatingSystemSection> + <VirtualHardwareSection> <Info>1 CPU, 1024 Memory</Info> <Item> <rasd:Caption>1 virtual cpu</rasd:Caption> @@ -66,7 +66,7 @@ <rasd:InstanceId>VOL</rasd:InstanceId> <rasd:ResourceType>17</rasd:ResourceType> <Type>disk</Type> - <rasd:HostResource>IMAGE/VOL</rasd:HostResource> + <rasd:HostResource>VOL</rasd:HostResource> <rasd:Parent>00000000-0000-0000-0000-000000000000</rasd:Parent> <rasd:Template>00000000-0000-0000-0000-000000000000</rasd:Template> <rasd:ApplicationList/> @@ -87,6 +87,6 @@ <rasd:Name>eth0</rasd:Name> <rasd:MACAddress>00:11:22:33:44:55</rasd:MACAddress> </Item> - </Section> - </Content> + </VirtualHardwareSection> + </VirtualSystem> </ovf:Envelope> diff --git a/v2v/test-v2v-o-vdsm-options.sh b/v2v/test-v2v-o-vdsm-options.sh index 44ad79a36..106f8694e 100755 --- a/v2v/test-v2v-o-vdsm-options.sh +++ b/v2v/test-v2v-o-vdsm-options.sh @@ -54,7 +54,8 @@ $VG virt-v2v --debug-gc \ --vdsm-vol-uuid VOL \ --vdsm-vm-uuid VM \ --vdsm-ovf-output $d/12345678-1234-1234-1234-123456789abc/master/vms/VM \ - --vdsm-compat=1.1 + --vdsm-compat=1.1 \ + --vdsm-ovf-flavour=ovirt # Test the OVF metadata was created. test -f $d/12345678-1234-1234-1234-123456789abc/master/vms/VM/VM.ovf diff --git a/v2v/virt-v2v.pod b/v2v/virt-v2v.pod index b2face339..db91a7a4c 100644 --- a/v2v/virt-v2v.pod +++ b/v2v/virt-v2v.pod @@ -759,6 +759,26 @@ hex digit can be C<0-9> or C<a-f>), conforming to S<OSF DCE 1.1>. These options can only be used with I<-o vdsm>. +=item B<--vdsm-ovf-flavour> flavour + +This option controls the format of the OVF generated at the end of conversion. +Currently there are two possible flavours: + +=over 4 + +=item rhevexp + +The OVF format used in RHV export storage domain. + +=item ovirt + +The OVF format understood by oVirt REST API. + +=back + +For backward compatibility the default is I<rhevexp>, but this may change in +the future. + =item B<-v> =item B<--verbose> -- 2.16.1
Tomáš Golembiovský
2018-Feb-22 10:42 UTC
Re: [Libguestfs] [PATCH v2 0/3] Make generated OVF more conforming to standard
On Thu, 22 Feb 2018 11:41:05 +0100 Tomáš Golembiovský <tgolembi@redhat.com> wrote:> The main reason for creating different OVF is that it can be used to create VM > by oVirt REST API. The RHV export domain flavor cannot be used that way. > > v1 -> v2: > - introduced flavour types instead of booleans > - instead of referring to the new flavour as "standard OVF" or "fixed OVF" I > refer to it as oVirt flavour. While it is more conforming than the one used > in export domain it can still contain some oVirt specific pieces.- I also added the list of flavours to --machine-readable output Tomas> > Tomáš Golembiovský (3): > v2v: tests: check generated OVF > v2v: ovf: Create OVF more aligned with the standard > v2v: vdsm: add --vdsm-fixed-ovf option > > v2v/Makefile.am | 2 + > v2v/cmdline.ml | 10 ++ > v2v/create_ovf.ml | 159 ++++++++++++++++++++++--------- > v2v/create_ovf.mli | 21 +++- > v2v/output_rhv.ml | 3 +- > v2v/output_vdsm.ml | 10 +- > v2v/output_vdsm.mli | 1 + > v2v/test-v2v-o-rhv.ovf.expected | 92 ++++++++++++++++++ > v2v/test-v2v-o-rhv.sh | 21 ++++ > v2v/test-v2v-o-vdsm-options.ovf.expected | 92 ++++++++++++++++++ > v2v/test-v2v-o-vdsm-options.sh | 20 +++- > v2v/virt-v2v.pod | 20 ++++ > 12 files changed, 400 insertions(+), 51 deletions(-) > create mode 100644 v2v/test-v2v-o-rhv.ovf.expected > create mode 100644 v2v/test-v2v-o-vdsm-options.ovf.expected > > -- > 2.16.1 >-- Tomáš Golembiovský <tgolembi@redhat.com>
Richard W.M. Jones
2018-Feb-22 14:24 UTC
Re: [Libguestfs] [PATCH v2 0/3] Make generated OVF more conforming to standard
ACK series. Will push after doing some tests. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones Read my programming and virtualization blog: http://rwmj.wordpress.com libguestfs lets you edit virtual machines. Supports shell scripting, bindings from many languages. http://libguestfs.org
Reasonably Related Threads
- [PATCH 0/3] Make generated OVF more conforming to standard
- [PATCH 1/2] Revert "v2v: oVirt changed the ResourceType for QXL video devices (RHBZ#1598715)."
- [PATCH INCOMPLETE 0/4] v2v: Add general mechanism for input and output options.
- [PATCH] v2v: ovf: Add virtio-rng and memory balloon device
- do we have support for xmlns in xml for v2v?