diff options
Diffstat (limited to 'src/VBox/ValidationKit/tests/api')
24 files changed, 2535 insertions, 0 deletions
diff --git a/src/VBox/ValidationKit/tests/api/Makefile.kmk b/src/VBox/ValidationKit/tests/api/Makefile.kmk new file mode 100644 index 00000000..2598e51a --- /dev/null +++ b/src/VBox/ValidationKit/tests/api/Makefile.kmk @@ -0,0 +1,72 @@ +# $Id: Makefile.kmk $ +## @file +# VirtualBox Validation Kit - API Tests. +# + +# +# Copyright (C) 2006-2023 Oracle and/or its affiliates. +# +# This file is part of VirtualBox base platform packages, as +# available from https://www.virtualbox.org. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, in version 3 of the +# License. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <https://www.gnu.org/licenses>. +# +# The contents of this file may alternatively be used under the terms +# of the Common Development and Distribution License Version 1.0 +# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included +# in the VirtualBox distribution, in which case the provisions of the +# CDDL are applicable instead of those of the GPL. +# +# You may elect to license modified versions of this file under the +# terms and conditions of either the GPL or the CDDL or both. +# +# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +# + +SUB_DEPTH = ../../../../.. +include $(KBUILD_PATH)/subheader.kmk + + +INSTALLS += ValidationKitTestsApi +ValidationKitTestsApi_TEMPLATE = VBoxValidationKitR3 +ValidationKitTestsApi_INST = $(INST_VALIDATIONKIT)tests/api/ +ValidationKitTestsApi_EXEC_SOURCES := \ + $(PATH_SUB_CURRENT)/tdApi1.py \ + $(PATH_SUB_CURRENT)/tdAppliance1.py \ + $(PATH_SUB_CURRENT)/tdCloneMedium1.py \ + $(PATH_SUB_CURRENT)/tdCreateVMWithDefaults1.py \ + $(PATH_SUB_CURRENT)/tdMoveMedium1.py \ + $(PATH_SUB_CURRENT)/tdMoveVm1.py \ + $(PATH_SUB_CURRENT)/tdPython1.py \ + $(PATH_SUB_CURRENT)/tdTreeDepth1.py + +ifndef VBOX_OSE + ValidationKitTestsApi_EXEC_SOURCES += \ + $(PATH_SUB_CURRENT)/tdCloud1.py \ + $(PATH_SUB_CURRENT)/tdOciConnection1.py \ + $(PATH_SUB_CURRENT)/tdOciExport1.py \ + $(PATH_SUB_CURRENT)/tdOciImage1.py \ + $(PATH_SUB_CURRENT)/tdOciImport1.py \ + $(PATH_SUB_CURRENT)/tdOciInstance1.py \ + $(PATH_SUB_CURRENT)/tdOciProfile1.py +endif +ValidationKitTestsApi_SOURCES := \ + $(wildcard \ + $(PATH_SUB_CURRENT)/*.ova \ + ) + +VBOX_VALIDATIONKIT_PYTHON_SOURCES += $(ValidationKitTestsApi_EXEC_SOURCES) + +$(evalcall def_vbox_validationkit_process_python_sources) +include $(FILE_KBUILD_SUB_FOOTER) diff --git a/src/VBox/ValidationKit/tests/api/__init__.py b/src/VBox/ValidationKit/tests/api/__init__.py new file mode 100644 index 00000000..db7ec260 --- /dev/null +++ b/src/VBox/ValidationKit/tests/api/__init__.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +# $Id: __init__.py $ + +""" +Just to make python 2.x happy. +""" + +__copyright__ = \ +""" +Copyright (C) 2012-2023 Oracle and/or its affiliates. + +This file is part of VirtualBox base platform packages, as +available from https://www.virtualbox.org. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation, in version 3 of the +License. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, see <https://www.gnu.org/licenses>. + +The contents of this file may alternatively be used under the terms +of the Common Development and Distribution License Version 1.0 +(CDDL), a copy of it is provided in the "COPYING.CDDL" file included +in the VirtualBox distribution, in which case the provisions of the +CDDL are applicable instead of those of the GPL. + +You may elect to license modified versions of this file under the +terms and conditions of either the GPL or the CDDL or both. + +SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +""" +__version__ = "$Revision: 155244 $" diff --git a/src/VBox/ValidationKit/tests/api/tdApi1.py b/src/VBox/ValidationKit/tests/api/tdApi1.py new file mode 100755 index 00000000..2aae19c8 --- /dev/null +++ b/src/VBox/ValidationKit/tests/api/tdApi1.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# $Id: tdApi1.py $ + +""" +VirtualBox Validation Kit - API Test wrapper #1 combining all API sub-tests +""" + +__copyright__ = \ +""" +Copyright (C) 2010-2023 Oracle and/or its affiliates. + +This file is part of VirtualBox base platform packages, as +available from https://www.virtualbox.org. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation, in version 3 of the +License. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, see <https://www.gnu.org/licenses>. + +The contents of this file may alternatively be used under the terms +of the Common Development and Distribution License Version 1.0 +(CDDL), a copy of it is provided in the "COPYING.CDDL" file included +in the VirtualBox distribution, in which case the provisions of the +CDDL are applicable instead of those of the GPL. + +You may elect to license modified versions of this file under the +terms and conditions of either the GPL or the CDDL or both. + +SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +""" +__version__ = "$Revision: 155244 $" + + +# Standard Python imports. +import os +import sys + +# Only the main script needs to modify the path. +try: __file__ +except: __file__ = sys.argv[0] +g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +sys.path.append(g_ksValidationKitDir) + +# Validation Kit imports. +from testdriver import vbox + + +class tdApi1(vbox.TestDriver): + """ + API Test wrapper #1. + """ + + def __init__(self, aoSubTestDriverClasses = None): + vbox.TestDriver.__init__(self) + for oSubTestDriverClass in aoSubTestDriverClasses: + self.addSubTestDriver(oSubTestDriverClass(self)); + + # + # Overridden methods. + # + + def actionConfig(self): + """ + Import the API. + """ + if not self.importVBoxApi(): + return False + return True + + def actionExecute(self): + """ + Execute the testcase, i.e. all sub-tests. + """ + fRc = True; + for oSubTstDrv in self.aoSubTstDrvs: + if oSubTstDrv.fEnabled: + fRc = oSubTstDrv.testIt() and fRc; + return fRc; + + +if __name__ == '__main__': + sys.path.append(os.path.dirname(os.path.abspath(__file__))) + from tdPython1 import SubTstDrvPython1; # pylint: disable=relative-import + from tdAppliance1 import SubTstDrvAppliance1; # pylint: disable=relative-import + from tdMoveMedium1 import SubTstDrvMoveMedium1; # pylint: disable=relative-import + from tdTreeDepth1 import SubTstDrvTreeDepth1; # pylint: disable=relative-import + from tdMoveVm1 import SubTstDrvMoveVm1; # pylint: disable=relative-import + from tdCloneMedium1 import SubTstDrvCloneMedium1;# pylint: disable=relative-import + sys.exit(tdApi1([SubTstDrvPython1, SubTstDrvAppliance1, SubTstDrvMoveMedium1, + SubTstDrvTreeDepth1, SubTstDrvMoveVm1, SubTstDrvCloneMedium1]).main(sys.argv)) diff --git a/src/VBox/ValidationKit/tests/api/tdAppliance1-t1.ova b/src/VBox/ValidationKit/tests/api/tdAppliance1-t1.ova Binary files differnew file mode 100644 index 00000000..aba10dbb --- /dev/null +++ b/src/VBox/ValidationKit/tests/api/tdAppliance1-t1.ova diff --git a/src/VBox/ValidationKit/tests/api/tdAppliance1-t2-ovftool-4.1.0.ova b/src/VBox/ValidationKit/tests/api/tdAppliance1-t2-ovftool-4.1.0.ova Binary files differnew file mode 100644 index 00000000..19c2c4b9 --- /dev/null +++ b/src/VBox/ValidationKit/tests/api/tdAppliance1-t2-ovftool-4.1.0.ova diff --git a/src/VBox/ValidationKit/tests/api/tdAppliance1-t2.ova b/src/VBox/ValidationKit/tests/api/tdAppliance1-t2.ova Binary files differnew file mode 100644 index 00000000..66a173aa --- /dev/null +++ b/src/VBox/ValidationKit/tests/api/tdAppliance1-t2.ova diff --git a/src/VBox/ValidationKit/tests/api/tdAppliance1-t3-ovftool-4.1.0.ova b/src/VBox/ValidationKit/tests/api/tdAppliance1-t3-ovftool-4.1.0.ova Binary files differnew file mode 100644 index 00000000..8027ce64 --- /dev/null +++ b/src/VBox/ValidationKit/tests/api/tdAppliance1-t3-ovftool-4.1.0.ova diff --git a/src/VBox/ValidationKit/tests/api/tdAppliance1-t3.ova b/src/VBox/ValidationKit/tests/api/tdAppliance1-t3.ova Binary files differnew file mode 100644 index 00000000..7fbf44ee --- /dev/null +++ b/src/VBox/ValidationKit/tests/api/tdAppliance1-t3.ova diff --git a/src/VBox/ValidationKit/tests/api/tdAppliance1-t4-ovftool-4.1.0.ova b/src/VBox/ValidationKit/tests/api/tdAppliance1-t4-ovftool-4.1.0.ova Binary files differnew file mode 100644 index 00000000..2434f37e --- /dev/null +++ b/src/VBox/ValidationKit/tests/api/tdAppliance1-t4-ovftool-4.1.0.ova diff --git a/src/VBox/ValidationKit/tests/api/tdAppliance1-t4.ova b/src/VBox/ValidationKit/tests/api/tdAppliance1-t4.ova Binary files differnew file mode 100644 index 00000000..a6549b15 --- /dev/null +++ b/src/VBox/ValidationKit/tests/api/tdAppliance1-t4.ova diff --git a/src/VBox/ValidationKit/tests/api/tdAppliance1-t4.pem b/src/VBox/ValidationKit/tests/api/tdAppliance1-t4.pem new file mode 100644 index 00000000..c155d659 --- /dev/null +++ b/src/VBox/ValidationKit/tests/api/tdAppliance1-t4.pem @@ -0,0 +1,74 @@ +-----BEGIN PRIVATE KEY----- +MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBALRXOpwrjjFzFZtb +aTtB+3kNrgrqYkye6CqvrOODZwXsljr/pduYj6bYh5OdznAFqgeeCpkqEf/2p5mD +XlqE4cDoL2opLk7LrdmVDqX9rUkGcGf6hwTp1EAllESBIUt6LdSbL7Y5goYq1E++ +/fKSYRIniCbWp9ahTLH8hjDcw48tAgMBAAECgYAiNl4vHHA4X13dAEWBcW4UtAyt +k3Ocl5Tx7Cv/aYFU9WI2xSMg+ttdyrxFu+1bASgVk9zs27dYeOGo1OxEfesZzQkT +mbzvYCdYk9wAWKXQwpp78HZyEsKVipIxO+riH9ph7SFQBzB5NoADPoqwahOmeQQW +sE3oTJRa9O+JR3muXQJBAOOt4dj+1Rwmdy83j9uOLLfO75l2pJd/hq5gN7+eNFks +R68cbhFGkrOU13dVLquyqxAoaKc2PgZS+RfGRrohjm8CQQDKxeuqYXyrYBU4jNAS +TgcR4lbb8HiC1AyQrdiJVtH4qLpqk92M7muHXZQGOXRizHQMrRDY+PcgLoFAnRzJ +j0ojAkEA3rqL5ivlbtRyY86G/NHpDSdzXT2jZlFq/8tAvkOWEmYu+i9lvaC8gtFo +t2Stc2olzni5aFq38pfY9lkRd6S8IQJBAJe3LJPnqwrish4Epa38eae07P5U1yY0 +GE6r9EcWEbZ2MDyL9Al9XjEDIDzkAiPmC7JsTx24cda/VPAOXbqlnncCQQCCevzg +rrnTz0/3iLWkxowiKCE1EvbVALDxPwHi0zvjXbGZs8BodLxeChpJzFImbxRVAIrp +WvZOuLqhAKjL7OOT +-----END PRIVATE KEY----- +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 14180722474914962380 (0xc4cc0bf54fb45bcc) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=DE, ST=Example, L=For Instance, O=Beispiel GmbH, CN=beispiel.example.org + Validity + Not Before: Feb 1 01:45:27 2016 GMT + Not After : Jan 24 01:45:27 2046 GMT + Subject: C=DE, ST=Example, L=For Instance, O=Beispiel GmbH, CN=beispiel.example.org + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (1024 bit) + Modulus: + 00:b4:57:3a:9c:2b:8e:31:73:15:9b:5b:69:3b:41: + fb:79:0d:ae:0a:ea:62:4c:9e:e8:2a:af:ac:e3:83: + 67:05:ec:96:3a:ff:a5:db:98:8f:a6:d8:87:93:9d: + ce:70:05:aa:07:9e:0a:99:2a:11:ff:f6:a7:99:83: + 5e:5a:84:e1:c0:e8:2f:6a:29:2e:4e:cb:ad:d9:95: + 0e:a5:fd:ad:49:06:70:67:fa:87:04:e9:d4:40:25: + 94:44:81:21:4b:7a:2d:d4:9b:2f:b6:39:82:86:2a: + d4:4f:be:fd:f2:92:61:12:27:88:26:d6:a7:d6:a1: + 4c:b1:fc:86:30:dc:c3:8f:2d + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Key Identifier: + 02:0A:B0:BC:21:63:C1:50:16:1E:8D:B7:F4:B0:1C:48:D8:E1:0A:2A + X509v3 Authority Key Identifier: + keyid:02:0A:B0:BC:21:63:C1:50:16:1E:8D:B7:F4:B0:1C:48:D8:E1:0A:2A + + X509v3 Basic Constraints: + CA:TRUE + Signature Algorithm: sha256WithRSAEncryption + 13:f1:33:5f:c7:d9:6c:20:a4:eb:f2:e5:e2:b5:e0:e8:b6:9c: + c7:62:4a:39:53:83:11:98:cf:11:3d:58:09:d8:38:78:71:16: + d4:24:cc:c8:2e:5a:2b:d3:94:6a:dc:ae:62:e7:81:6a:5f:04: + 84:ba:55:8c:dc:6b:ff:aa:78:4f:37:8e:fd:ba:b5:d1:27:83: + 47:29:30:92:63:85:53:f0:b1:b9:f4:c7:a8:b1:48:44:4e:30: + 6f:50:d3:35:14:87:59:d0:f8:ed:da:07:60:6c:de:6d:53:53: + 3d:d7:03:97:1f:6b:13:ce:92:49:20:57:4f:b0:87:30:76:66: + d3:43 +-----BEGIN CERTIFICATE----- +MIICqDCCAhGgAwIBAgIJAMTMC/VPtFvMMA0GCSqGSIb3DQEBCwUAMG0xCzAJBgNV +BAYTAkRFMRAwDgYDVQQIDAdFeGFtcGxlMRUwEwYDVQQHDAxGb3IgSW5zdGFuY2Ux +FjAUBgNVBAoMDUJlaXNwaWVsIEdtYkgxHTAbBgNVBAMMFGJlaXNwaWVsLmV4YW1w +bGUub3JnMB4XDTE2MDIwMTAxNDUyN1oXDTQ2MDEyNDAxNDUyN1owbTELMAkGA1UE +BhMCREUxEDAOBgNVBAgMB0V4YW1wbGUxFTATBgNVBAcMDEZvciBJbnN0YW5jZTEW +MBQGA1UECgwNQmVpc3BpZWwgR21iSDEdMBsGA1UEAwwUYmVpc3BpZWwuZXhhbXBs +ZS5vcmcwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALRXOpwrjjFzFZtbaTtB ++3kNrgrqYkye6CqvrOODZwXsljr/pduYj6bYh5OdznAFqgeeCpkqEf/2p5mDXlqE +4cDoL2opLk7LrdmVDqX9rUkGcGf6hwTp1EAllESBIUt6LdSbL7Y5goYq1E++/fKS +YRIniCbWp9ahTLH8hjDcw48tAgMBAAGjUDBOMB0GA1UdDgQWBBQCCrC8IWPBUBYe +jbf0sBxI2OEKKjAfBgNVHSMEGDAWgBQCCrC8IWPBUBYejbf0sBxI2OEKKjAMBgNV +HRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4GBABPxM1/H2WwgpOvy5eK14Oi2nMdi +SjlTgxGYzxE9WAnYOHhxFtQkzMguWivTlGrcrmLngWpfBIS6VYzca/+qeE83jv26 +tdEng0cpMJJjhVPwsbn0x6ixSEROMG9Q0zUUh1nQ+O3aB2Bs3m1TUz3XA5cfaxPO +kkkgV0+whzB2ZtND +-----END CERTIFICATE----- diff --git a/src/VBox/ValidationKit/tests/api/tdAppliance1-t5-ovftool-4.1.0.ova b/src/VBox/ValidationKit/tests/api/tdAppliance1-t5-ovftool-4.1.0.ova Binary files differnew file mode 100644 index 00000000..6a21a19b --- /dev/null +++ b/src/VBox/ValidationKit/tests/api/tdAppliance1-t5-ovftool-4.1.0.ova diff --git a/src/VBox/ValidationKit/tests/api/tdAppliance1-t5.ova b/src/VBox/ValidationKit/tests/api/tdAppliance1-t5.ova Binary files differnew file mode 100644 index 00000000..8e6e2e2e --- /dev/null +++ b/src/VBox/ValidationKit/tests/api/tdAppliance1-t5.ova diff --git a/src/VBox/ValidationKit/tests/api/tdAppliance1-t6-ovftool-4.1.0.ova b/src/VBox/ValidationKit/tests/api/tdAppliance1-t6-ovftool-4.1.0.ova Binary files differnew file mode 100644 index 00000000..d5a92eb2 --- /dev/null +++ b/src/VBox/ValidationKit/tests/api/tdAppliance1-t6-ovftool-4.1.0.ova diff --git a/src/VBox/ValidationKit/tests/api/tdAppliance1-t6.ova b/src/VBox/ValidationKit/tests/api/tdAppliance1-t6.ova Binary files differnew file mode 100644 index 00000000..6480e6d5 --- /dev/null +++ b/src/VBox/ValidationKit/tests/api/tdAppliance1-t6.ova diff --git a/src/VBox/ValidationKit/tests/api/tdAppliance1-t6.pem b/src/VBox/ValidationKit/tests/api/tdAppliance1-t6.pem new file mode 100644 index 00000000..1b682b0d --- /dev/null +++ b/src/VBox/ValidationKit/tests/api/tdAppliance1-t6.pem @@ -0,0 +1,134 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIJKAIBAAKCAgEAzYlueHZlbSqlLrVAnCyQx9xgnO7jSjK6wXqlbxkl0QZLyDnh +vNowZYnu7YZI3V+RuVzYbczlEr1qZpUeO8NF04mK1kFPJRinRiuiOtih+WwvDQd7 +7RZJhjoAcfEeogt1OzOZg8QKF1AMGlUgk5zWma4VIS75ZM6G1b77qS3rge6L9NwZ +gthyQ1PvddYue4MNEpK+2ekZZq/GGZvd+yNP0Wq7kzcmydCgCF9EdLCZwt4BHmOS +ZM6BUxKv+PINZgO7Wp2tBBGigg9Mf/NXofjMHp7RI16xa2W9Q/Ct2A6rb5SAB10U +ddrtxiI+F+Jv7lNkHOCLbju7cEs+Y6XIbt1l7aVqBFpgo6cwFZ7DdLPHZkgb50hx +dnNdwncOR1syspTF544hbmkcb/8+weq+qgsD/57b/ZGrwb8ruByDz0LZiXzxxBRe +w9OhHZGFnuC0D1ciIvpifKe4R/kLcEgVW7yYp4W1BAZgiBJPgXArJTlUBrxamzsQ +m6N7GxlP+vTCny7IJsQ1KWu5BqkPv0T1+zD5vUT9/lAa+XBdDHhYVm0adr4dXIef +6ws+0zbpz8DyaD/Bg9KvRKdnziVc+pKwkWOVrFvZ6NvBex0i8Bgh4FrpkdjmrZMZ +uuCdex0CEayuYexgUqyMg9m0GwGqVNG6mVB4cCOg0rayaqEKrbtUSxNaD/MCAwEA +AQKCAgAnjPGQBqBf0Fv2z/P92WmGu/ZvXFyqU3aycmpRJZKsVTzR66lvkMDNWSx7 +0mJFDvXYqHAROOM/pulJkho+P8Y4/XeU4P5c0hCmJRFTp4oLl/C53h3PsoE1bgXV +5yMQ5YmKedRpkZirgcDCdG0PWpfE/MWeHA7rgf5aNSTyGh7+YqvV02CpWAMsx4MF +ttA8/ivOziQhhIRZySsilGazw8jBMHulyXASV63jzok6txzvbY7jjR+HfGFQXgE0 +s0c2wTMVLdA0Pzx5MH51BJtxVJHato7h8n/LfclcwHyDXddJYlb8k8GzKAynGsG+ +ENmfD7btA5xw+teHtULtI+KcysepCeVXW7Fr7PFJe1XOx1c24WrFpjeGdnG3PdqA +Y9YbjMCD/GpCUICla3N6iRDG3imY+DUWcExVjUo6TUbQ4ofQT4WF7+2Yzg6MRqYu +41jgF+voCXS4BH8e+ngEjd8VLHma7FPuWujodCERsvxZ7aLXBcZCH1Lje9Y8a79x +RGX83pVHTWFRyo4wKBpRUz+hwH7MjYdfE2Q+Zl153pXw3au9jX8PUMhx9swWPGRw +CvLPwtTwrezzdFeXg4wfJwWnvqhKIViery4z3rdjh5Zke/9+7gXmFbZ8w+QUkXXq +cnBEJySTvpI4B9abpcEI4OO84L/A6ENDO10W4ZGfm5aD2m0vIQKCAQEA6nZAmsSr +T1CcgrWNcY6civ8o5cMF8fM9ib5QvibRlJN9dpcZPr6+Pp2UPCkjVowk/FybP1Kw +sqA0aXOL3VR03YpLrqdUDpe9YiFCa3ttDX0pTOdU9QW2RbOrNQVurKhzNETd37cJ +0RYBOHJY/RxA0F8l16wSr1jTO/SRbWkFJYZS/OaH1B6gCxz6H1IgfKJddrvFZsd8 +6PnLtqsPnsXuxugqHu3ENddRqiiSNyGnqpGu0jQpPBrQEQQN5o8/X9rfNfAobChH +Nii0va7MHudwaTTo2ytUzEV+v0kueMNU9YXgJgxzSzf1/VQC+HoNI4Vbs9XCbOWV +z2FVBwXM+0cIMQKCAQEA4Gr1pQAymJq/9p2fiWSR8TVdrYCwvAnBu8QSqSzweNZC +SRt7Nkhhljm7M2DuUOioMaFAT9CnyD2nJyKSAqqOngb0vo7C53UzgFu+QFMtUx/Z +t/C96WcLYrErXfnB/sTq0Gp4tpoo1S3FZecabhlZyv1O08WXiNCcEX1VMQz2+iF9 ++n2PjFuEomeWHxBWCwfB9/S0pKVHP/7833sQ8Dfsm2EwtPy3v58ln3UguaUDIe5V +r0TmKr8kDrYgbw1ZpBOd9Wbbvj3vIN34OPoSIrcar67DCp7qYq77LYAhngfdQGed +MUMsbU1dODWf0kwP7mncTr3mPByQUHa3ImjvJPv1YwKCAQBYa04Dz8U2/RR46pSz +zW9Vr9Ixi7GTRALiDkaO3z7MRC7daTAZDH/cRzre0TjFa8aK8TWO1NVUF7yMRAnr +5uzHm17dN7coZasC9b4BoKNIofnQSbEtUgEiGhanwSuyqzf+7zWpJ3LpSd4d9ml+ +0ofSzP8NbZQCUoIeqyWo2CEbvKNRQnLY2M/MQRpGc4dS2TxcCYXxM6v0hDeB5NLY +MpbQpj80OMB0+YWPoQs7BVMgrR37obYnN4ld0WSYnU7uDDF/OtlTqIDqeMFogyHx +SaCH3G8wMBAjlNWut59x5WAF033re2iDZlA7P9J6+DQ6QBGMKUHQJWiws2kIY/Sg +knIRAoIBAQCzlkR/Rxo2LthhZR/PFfEIQrl1Z9+GipRDSxPX2AOT33nqARjnhqK5 +UfexlOcBTj2SgcTyWjp6LoQ9+Bc6FPzODyj5+UqVaJ/PHxuvZCCIPZu/6+I+Dlz5 +HGhk6sJIu5JhOGLjVZhJiDhIZNkstBK8M1tKcvvh23aZNF/hQcu+vOCQfLxMCMyq +HhTvROZmK04Yu/V3MGBFISuBN32FjmtEqFEO9JGiwZuc8GFAzoEkPRLKkGtUV+Nl +9m8cD2XlvGESib5djjh3Z8oE5nFu4HJ1lne0XxmX4QlWDwxX51kx+fi7/FJoIZnw +qlD8PCwfkQ1g4eyFvCHskiPZYHnHce2bAoIBACW40wFIPiDRhjNywEi4miN6P+Xs +rlM9csmxw2GG6Z4c8H/Z4SNBRQmnj/F6PHsar6SGy9WlR9mNeR2XBn4Pyf/cNjTQ +bKzV5wPRm6P81SQjhIz4Mxdx1S30AeF1LdagWFiq0on7oRTH2SeKQspdpNeiTDbC +sBw4SVDKmGZYGZcuT3BdvvZFEW4qncSYuUM7l9bTmsbzid/v8zn/XDQrpdPYnptD +ljJETKQzlyrJLtTbyFlo3Osf1N4408u3rqhpw2SgKdyMiHndhxkF869Vycll+VMz +SzPU0wI62BIPWHDBJLnxGBTa+4kUSxP+oDvCfVYCmDcfDRew3MWCK9emJnU= +-----END RSA PRIVATE KEY----- +Certificate: + Data: + Version: 1 (0x0) + Serial Number: 1 (0x1) + Signature Algorithm: sha512WithRSAEncryption + Issuer: C=DE, ST=Example, L=For Instance, O=Beispiel GmbH, CN=beispiel.example.org + Validity + Not Before: Feb 15 14:27:56 2016 GMT + Not After : Feb 2 14:27:56 2066 GMT + Subject: C=DE, ST=Instance-Example, L=Examplecity, O=For Instance GmbH, OU=The Example Unit, CN=subcert.example.org/emailAddress=subcert@example.org + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (4096 bit) + Modulus: + 00:cd:89:6e:78:76:65:6d:2a:a5:2e:b5:40:9c:2c: + 90:c7:dc:60:9c:ee:e3:4a:32:ba:c1:7a:a5:6f:19: + 25:d1:06:4b:c8:39:e1:bc:da:30:65:89:ee:ed:86: + 48:dd:5f:91:b9:5c:d8:6d:cc:e5:12:bd:6a:66:95: + 1e:3b:c3:45:d3:89:8a:d6:41:4f:25:18:a7:46:2b: + a2:3a:d8:a1:f9:6c:2f:0d:07:7b:ed:16:49:86:3a: + 00:71:f1:1e:a2:0b:75:3b:33:99:83:c4:0a:17:50: + 0c:1a:55:20:93:9c:d6:99:ae:15:21:2e:f9:64:ce: + 86:d5:be:fb:a9:2d:eb:81:ee:8b:f4:dc:19:82:d8: + 72:43:53:ef:75:d6:2e:7b:83:0d:12:92:be:d9:e9: + 19:66:af:c6:19:9b:dd:fb:23:4f:d1:6a:bb:93:37: + 26:c9:d0:a0:08:5f:44:74:b0:99:c2:de:01:1e:63: + 92:64:ce:81:53:12:af:f8:f2:0d:66:03:bb:5a:9d: + ad:04:11:a2:82:0f:4c:7f:f3:57:a1:f8:cc:1e:9e: + d1:23:5e:b1:6b:65:bd:43:f0:ad:d8:0e:ab:6f:94: + 80:07:5d:14:75:da:ed:c6:22:3e:17:e2:6f:ee:53: + 64:1c:e0:8b:6e:3b:bb:70:4b:3e:63:a5:c8:6e:dd: + 65:ed:a5:6a:04:5a:60:a3:a7:30:15:9e:c3:74:b3: + c7:66:48:1b:e7:48:71:76:73:5d:c2:77:0e:47:5b: + 32:b2:94:c5:e7:8e:21:6e:69:1c:6f:ff:3e:c1:ea: + be:aa:0b:03:ff:9e:db:fd:91:ab:c1:bf:2b:b8:1c: + 83:cf:42:d9:89:7c:f1:c4:14:5e:c3:d3:a1:1d:91: + 85:9e:e0:b4:0f:57:22:22:fa:62:7c:a7:b8:47:f9: + 0b:70:48:15:5b:bc:98:a7:85:b5:04:06:60:88:12: + 4f:81:70:2b:25:39:54:06:bc:5a:9b:3b:10:9b:a3: + 7b:1b:19:4f:fa:f4:c2:9f:2e:c8:26:c4:35:29:6b: + b9:06:a9:0f:bf:44:f5:fb:30:f9:bd:44:fd:fe:50: + 1a:f9:70:5d:0c:78:58:56:6d:1a:76:be:1d:5c:87: + 9f:eb:0b:3e:d3:36:e9:cf:c0:f2:68:3f:c1:83:d2: + af:44:a7:67:ce:25:5c:fa:92:b0:91:63:95:ac:5b: + d9:e8:db:c1:7b:1d:22:f0:18:21:e0:5a:e9:91:d8: + e6:ad:93:19:ba:e0:9d:7b:1d:02:11:ac:ae:61:ec: + 60:52:ac:8c:83:d9:b4:1b:01:aa:54:d1:ba:99:50: + 78:70:23:a0:d2:b6:b2:6a:a1:0a:ad:bb:54:4b:13: + 5a:0f:f3 + Exponent: 65537 (0x10001) + Signature Algorithm: sha512WithRSAEncryption + 2d:1c:49:41:1a:4f:dc:d8:5c:b1:fa:ca:53:38:86:26:6e:56: + a5:6d:2e:1d:0d:74:64:0e:89:c3:3a:c7:1d:01:6e:d1:93:b2: + c9:37:01:6a:ae:31:42:96:05:d7:df:fd:01:f8:bc:f3:f3:4c: + cd:75:ae:16:00:61:78:f2:67:c5:b1:76:76:16:39:ba:d2:6b: + 09:ad:99:2b:22:ce:56:89:4d:08:ca:8c:76:4c:50:6b:83:c9: + 46:9b:f5:9f:2d:e2:7f:e5:72:aa:76:56:c4:67:83:45:26:b7: + e2:ae:f7:1e:61:c9:aa:2e:8d:b8:59:42:84:37:25:c8:16:92: + d6:d5 +-----BEGIN CERTIFICATE----- +MIIEGjCCA4MCAQEwDQYJKoZIhvcNAQENBQAwbTELMAkGA1UEBhMCREUxEDAOBgNV +BAgMB0V4YW1wbGUxFTATBgNVBAcMDEZvciBJbnN0YW5jZTEWMBQGA1UECgwNQmVp +c3BpZWwgR21iSDEdMBsGA1UEAwwUYmVpc3BpZWwuZXhhbXBsZS5vcmcwIBcNMTYw +MjE1MTQyNzU2WhgPMjA2NjAyMDIxNDI3NTZaMIG3MQswCQYDVQQGEwJERTEZMBcG +A1UECAwQSW5zdGFuY2UtRXhhbXBsZTEUMBIGA1UEBwwLRXhhbXBsZWNpdHkxGjAY +BgNVBAoMEUZvciBJbnN0YW5jZSBHbWJIMRkwFwYDVQQLDBBUaGUgRXhhbXBsZSBV +bml0MRwwGgYDVQQDDBNzdWJjZXJ0LmV4YW1wbGUub3JnMSIwIAYJKoZIhvcNAQkB +FhNzdWJjZXJ0QGV4YW1wbGUub3JnMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC +CgKCAgEAzYlueHZlbSqlLrVAnCyQx9xgnO7jSjK6wXqlbxkl0QZLyDnhvNowZYnu +7YZI3V+RuVzYbczlEr1qZpUeO8NF04mK1kFPJRinRiuiOtih+WwvDQd77RZJhjoA +cfEeogt1OzOZg8QKF1AMGlUgk5zWma4VIS75ZM6G1b77qS3rge6L9NwZgthyQ1Pv +ddYue4MNEpK+2ekZZq/GGZvd+yNP0Wq7kzcmydCgCF9EdLCZwt4BHmOSZM6BUxKv ++PINZgO7Wp2tBBGigg9Mf/NXofjMHp7RI16xa2W9Q/Ct2A6rb5SAB10UddrtxiI+ +F+Jv7lNkHOCLbju7cEs+Y6XIbt1l7aVqBFpgo6cwFZ7DdLPHZkgb50hxdnNdwncO +R1syspTF544hbmkcb/8+weq+qgsD/57b/ZGrwb8ruByDz0LZiXzxxBRew9OhHZGF +nuC0D1ciIvpifKe4R/kLcEgVW7yYp4W1BAZgiBJPgXArJTlUBrxamzsQm6N7GxlP ++vTCny7IJsQ1KWu5BqkPv0T1+zD5vUT9/lAa+XBdDHhYVm0adr4dXIef6ws+0zbp +z8DyaD/Bg9KvRKdnziVc+pKwkWOVrFvZ6NvBex0i8Bgh4FrpkdjmrZMZuuCdex0C +EayuYexgUqyMg9m0GwGqVNG6mVB4cCOg0rayaqEKrbtUSxNaD/MCAwEAATANBgkq +hkiG9w0BAQ0FAAOBgQAtHElBGk/c2Fyx+spTOIYmblalbS4dDXRkDonDOscdAW7R +k7LJNwFqrjFClgXX3/0B+Lzz80zNda4WAGF48mfFsXZ2Fjm60msJrZkrIs5WiU0I +yox2TFBrg8lGm/WfLeJ/5XKqdlbEZ4NFJrfirvceYcmqLo24WUKENyXIFpLW1Q== +-----END CERTIFICATE----- diff --git a/src/VBox/ValidationKit/tests/api/tdAppliance1-t7-bad-instance.ova b/src/VBox/ValidationKit/tests/api/tdAppliance1-t7-bad-instance.ova Binary files differnew file mode 100644 index 00000000..48e49059 --- /dev/null +++ b/src/VBox/ValidationKit/tests/api/tdAppliance1-t7-bad-instance.ova diff --git a/src/VBox/ValidationKit/tests/api/tdAppliance1.py b/src/VBox/ValidationKit/tests/api/tdAppliance1.py new file mode 100755 index 00000000..a92d8b42 --- /dev/null +++ b/src/VBox/ValidationKit/tests/api/tdAppliance1.py @@ -0,0 +1,217 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# $Id: tdAppliance1.py $ + +""" +VirtualBox Validation Kit - IAppliance Test #1 +""" + +__copyright__ = \ +""" +Copyright (C) 2010-2023 Oracle and/or its affiliates. + +This file is part of VirtualBox base platform packages, as +available from https://www.virtualbox.org. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation, in version 3 of the +License. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, see <https://www.gnu.org/licenses>. + +The contents of this file may alternatively be used under the terms +of the Common Development and Distribution License Version 1.0 +(CDDL), a copy of it is provided in the "COPYING.CDDL" file included +in the VirtualBox distribution, in which case the provisions of the +CDDL are applicable instead of those of the GPL. + +You may elect to license modified versions of this file under the +terms and conditions of either the GPL or the CDDL or both. + +SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +""" +__version__ = "$Revision: 155244 $" + + +# Standard Python imports. +import os +import sys +import tarfile + +# Only the main script needs to modify the path. +try: __file__ +except: __file__ = sys.argv[0] +g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +sys.path.append(g_ksValidationKitDir) + +# Validation Kit imports. +from testdriver import base; +from testdriver import reporter; +from testdriver import vboxwrappers; + + +class SubTstDrvAppliance1(base.SubTestDriverBase): + """ + Sub-test driver for IAppliance Test #1. + """ + + def __init__(self, oTstDrv): + base.SubTestDriverBase.__init__(self, oTstDrv, 'appliance', 'Applicance'); + + def testIt(self): + """ + Execute the sub-testcase. + """ + fRc = True; + + # Import a set of simple OVAs. + # Note! Manifests generated by ovftool 4.0.0 does not include the ovf, while the ones b 4.1.0 does. + for sOva in ( + # t1 is a plain VM without any disks, ovftool 4.0 export from fusion + 'tdAppliance1-t1.ova', + # t2 is a plain VM with one disk. Both 4.0 and 4.1.0 exports. + 'tdAppliance1-t2.ova', + 'tdAppliance1-t2-ovftool-4.1.0.ova', + # t3 is a VM with one gzipped disk and selecting SHA256 on the ovftool cmdline (--compress=9 --shaAlgorithm=sha256). + 'tdAppliance1-t3.ova', + 'tdAppliance1-t3-ovftool-4.1.0.ova', + # t4 is a VM with with two gzipped disk, SHA256 and a (self) signed manifest (--privateKey=./tdAppliance1-t4.pem). + 'tdAppliance1-t4.ova', + 'tdAppliance1-t4-ovftool-4.1.0.ova', + # t5 is a VM with with one gzipped disk, SHA1 and a manifest signed by a valid (2016) DigiCert code signing cert. + 'tdAppliance1-t5.ova', + 'tdAppliance1-t5-ovftool-4.1.0.ova', + # t6 is a VM with with one gzipped disk, SHA1 and a manifest signed by a certificate issued by the t4 certificate, + # thus it should be impossible to establish a trusted path to a root CA. + 'tdAppliance1-t6.ova', + 'tdAppliance1-t6-ovftool-4.1.0.ova', + # t7 is based on tdAppliance1-t2-ovftool-4.1.0.ova and has modified to have an invalid InstanceID as well as an + # extra readme file. It was tarred up using bsdtar 2.4.12 on windows, so it uses a slightly different tar format and + # have different file attributes. + 'tdAppliance1-t7-bad-instance.ova', + ): + reporter.testStart(sOva); + try: + fRc = self.testImportOva(os.path.join(g_ksValidationKitDir, 'tests', 'api', sOva)) and fRc; + fRc = self.testImportOvaAsOvf(os.path.join(g_ksValidationKitDir, 'tests', 'api', sOva)) and fRc; + except: + reporter.errorXcpt(); + fRc = False; + fRc = reporter.testDone() and fRc; + + ## @todo more stuff + return fRc; + + # + # Test execution helpers. + # + + def testImportOva(self, sOva): + """ xxx """ + oVirtualBox = self.oTstDrv.oVBoxMgr.getVirtualBox(); + + # + # Import it as OVA. + # + try: + oAppliance = oVirtualBox.createAppliance(); + except: + return reporter.errorXcpt('IVirtualBox::createAppliance failed'); + + try: + oProgress = vboxwrappers.ProgressWrapper(oAppliance.read(sOva), self.oTstDrv.oVBoxMgr, self.oTstDrv, + 'read "%s"' % (sOva,)); + except: + return reporter.errorXcpt('IAppliance::read("%s") failed' % (sOva,)); + oProgress.wait(); + if oProgress.logResult() is False: + return False; + + try: + oAppliance.interpret(); + except: + return reporter.errorXcpt('IAppliance::interpret() failed on "%s"' % (sOva,)); + + # + try: + oProgress = vboxwrappers.ProgressWrapper(oAppliance.importMachines([]), + self.oTstDrv.oVBoxMgr, self.oTstDrv, 'importMachines "%s"' % (sOva,)); + except: + return reporter.errorXcpt('IAppliance::importMachines failed on "%s"' % (sOva,)); + oProgress.wait(); + if oProgress.logResult() is False: + return False; + + # + # Export the + # + ## @todo do more with this OVA. Like untaring it and loading it as an OVF. Export it and import it again. + + return True; + + def testImportOvaAsOvf(self, sOva): + """ + Unpacks the OVA into a subdirectory in the scratch area and imports it as an OVF. + """ + oVirtualBox = self.oTstDrv.oVBoxMgr.getVirtualBox(); + + sTmpDir = os.path.join(self.oTstDrv.sScratchPath, os.path.split(sOva)[1] + '-ovf'); + sOvf = os.path.join(sTmpDir, os.path.splitext(os.path.split(sOva)[1])[0] + '.ovf'); + + # + # Unpack + # + try: + os.mkdir(sTmpDir, 0o755); + oTarFile = tarfile.open(sOva, 'r:*'); # No 'with' support in 2.6. pylint: disable=consider-using-with + oTarFile.extractall(sTmpDir); + oTarFile.close(); + except: + return reporter.errorXcpt('Unpacking "%s" to "%s" for OVF style importing failed' % (sOvf, sTmpDir,)); + + # + # Import. + # + try: + oAppliance2 = oVirtualBox.createAppliance(); + except: + return reporter.errorXcpt('IVirtualBox::createAppliance failed (#2)'); + + try: + oProgress = vboxwrappers.ProgressWrapper(oAppliance2.read(sOvf), self.oTstDrv.oVBoxMgr, self.oTstDrv, + 'read "%s"' % (sOvf,)); + except: + return reporter.errorXcpt('IAppliance::read("%s") failed' % (sOvf,)); + oProgress.wait(); + if oProgress.logResult() is False: + return False; + + try: + oAppliance2.interpret(); + except: + return reporter.errorXcpt('IAppliance::interpret() failed on "%s"' % (sOvf,)); + + try: + oProgress = vboxwrappers.ProgressWrapper(oAppliance2.importMachines([]), + self.oTstDrv.oVBoxMgr, self.oTstDrv, 'importMachines "%s"' % (sOvf,)); + except: + return reporter.errorXcpt('IAppliance::importMachines failed on "%s"' % (sOvf,)); + oProgress.wait(); + if oProgress.logResult() is False: + return False; + + return True; + + +if __name__ == '__main__': + sys.path.append(os.path.dirname(os.path.abspath(__file__))) + from tests.api.tdApi1 import tdApi1; + sys.exit(tdApi1([SubTstDrvAppliance1]).main(sys.argv)); + diff --git a/src/VBox/ValidationKit/tests/api/tdCloneMedium1.py b/src/VBox/ValidationKit/tests/api/tdCloneMedium1.py new file mode 100755 index 00000000..056fb2eb --- /dev/null +++ b/src/VBox/ValidationKit/tests/api/tdCloneMedium1.py @@ -0,0 +1,243 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# $Id: tdCloneMedium1.py $ + +""" +VirtualBox Validation Kit - Clone Medium Test #1 +""" + +__copyright__ = \ +""" +Copyright (C) 2010-2023 Oracle and/or its affiliates. + +This file is part of VirtualBox base platform packages, as +available from https://www.virtualbox.org. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation, in version 3 of the +License. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, see <https://www.gnu.org/licenses>. + +The contents of this file may alternatively be used under the terms +of the Common Development and Distribution License Version 1.0 +(CDDL), a copy of it is provided in the "COPYING.CDDL" file included +in the VirtualBox distribution, in which case the provisions of the +CDDL are applicable instead of those of the GPL. + +You may elect to license modified versions of this file under the +terms and conditions of either the GPL or the CDDL or both. + +SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +""" +__version__ = "$Revision: 155244 $" + + +# Standard Python imports. +import os +import sys + +# Only the main script needs to modify the path. +try: __file__ +except: __file__ = sys.argv[0] +g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +sys.path.append(g_ksValidationKitDir) + +# Validation Kit imports. +from testdriver import base +from testdriver import reporter +from testdriver import vboxcon +from testdriver import vboxwrappers + + +class SubTstDrvCloneMedium1(base.SubTestDriverBase): + """ + Sub-test driver for Clone Medium Test #1. + """ + + def __init__(self, oTstDrv): + base.SubTestDriverBase.__init__(self, oTstDrv, 'clone-medium', 'Move Medium'); + + def testIt(self): + """ + Execute the sub-testcase. + """ + + return self.testAll() + + # + # Test execution helpers. + # + + def createTestMedium(self, oVM, sPathSuffix, sFmt = 'VDI', cbSize = 1024*1024, data = None): + assert oVM is not None + + oSession = self.oTstDrv.openSession(oVM) + + if oSession is None: + return False + + # + # Create Medium Object + # + + sBaseHdd1Path = os.path.join(self.oTstDrv.sScratchPath, sPathSuffix) + sBaseHdd1Fmt = sFmt + cbBaseHdd1Size = cbSize + + try: + oBaseHdd1 = oSession.createBaseHd(sBaseHdd1Path, sBaseHdd1Fmt, cbBaseHdd1Size) + except: + return reporter.errorXcpt('createBaseHd failed') + + oMediumIOBaseHdd1 = oBaseHdd1.openForIO(True, "") + + if data: + cbWritten = oMediumIOBaseHdd1.write(0, data) + + if cbWritten != 1: + return reporter.error("Failed writing to test hdd.") + + oMediumIOBaseHdd1.close() + + return oBaseHdd1 + + def cloneMedium(self, oSrcHd, oTgtHd): + """ + Clones medium into target medium. + """ + try: + oProgressCom = oSrcHd.cloneTo(oTgtHd, (vboxcon.MediumVariant_Standard, ), None); + except: + reporter.errorXcpt('failed to clone medium %s to %s' % (oSrcHd.name, oTgtHd.name)); + return False; + oProgress = vboxwrappers.ProgressWrapper(oProgressCom, self.oTstDrv.oVBoxMgr, self.oTstDrv, + 'clone base disk %s to %s' % (oSrcHd.name, oTgtHd.name)); + oProgress.wait(cMsTimeout = 15*60*1000); # 15 min + oProgress.logResult(); + return True; + + def resizeAndCloneMedium(self, oSrcHd, oTgtHd, cbTgtSize): + """ + Clones medium into target medium. + """ + + try: + oProgressCom = oSrcHd.resizeAndCloneTo(oTgtHd, cbTgtSize, (vboxcon.MediumVariant_Standard, ), None); + except: + reporter.errorXcpt('failed to resize and clone medium %s to %s and to size %d' \ + % (oSrcHd.name, oTgtHd.name, cbTgtSize)); + return False; + oProgress = vboxwrappers.ProgressWrapper(oProgressCom, self.oTstDrv.oVBoxMgr, self.oTstDrv, + 'resize and clone base disk %s to %s and to size %d' \ + % (oSrcHd.name, oTgtHd.name, cbTgtSize)); + oProgress.wait(cMsTimeout = 15*60*1000); # 15 min + oProgress.logResult(); + return True; + + def deleteVM(self, oVM): + try: + oVM.unregister(vboxcon.CleanupMode_DetachAllReturnNone); + except: + reporter.logXcpt(); + + try: + oProgressCom = oVM.deleteConfig([]); + except: + reporter.logXcpt(); + else: + oProgress = vboxwrappers.ProgressWrapper(oProgressCom, self.oTstDrv.oVBoxMgr, self.oTstDrv, + 'Delete VM %s' % (oVM.name)); + oProgress.wait(cMsTimeout = 15*60*1000); # 15 min + oProgress.logResult(); + + return None; + + # + # Tests + # + + def testCloneOnly(self): + """ + Tests cloning mediums only. No resize. + """ + + reporter.testStart("testCloneOnly") + + oVM = self.oTstDrv.createTestVM('test-medium-clone-only', 1, None, 4) + + hd1 = self.createTestMedium(oVM, "hd1-cloneonly", data=[0xdeadbeef]) + hd2 = self.createTestMedium(oVM, "hd2-cloneonly") + + if not self.cloneMedium(hd1, hd2): + return False + + oMediumIOhd1 = hd1.openForIO(True, "") + dataHd1 = oMediumIOhd1.read(0, 4) + oMediumIOhd1.close() + + oMediumIOhd2 = hd2.openForIO(True, "") + dataHd2 = oMediumIOhd2.read(0, 4) + oMediumIOhd2.close() + + if dataHd1 != dataHd2: + reporter.testFailure("Data read is unexpected.") + + self.deleteVM(oVM) + + reporter.testDone() + return True + + def testResizeAndClone(self): + """ + Tests resizing and cloning mediums only. + """ + + reporter.testStart("testResizeAndClone") + + oVM = self.oTstDrv.createTestVM('test-medium-clone-only', 1, None, 4) + + hd1 = self.createTestMedium(oVM, "hd1-resizeandclone", data=[0xdeadbeef]) + hd2 = self.createTestMedium(oVM, "hd2-resizeandclone") + + if not (hasattr(hd1, "resizeAndCloneTo") and callable(getattr(hd1, "resizeAndCloneTo"))): + self.deleteVM(oVM) + reporter.testDone() + return True + + if not self.resizeAndCloneMedium(hd1, hd2, 1024*1024*2): + return False + + oMediumIOhd1 = hd1.openForIO(True, "") + dataHd1 = oMediumIOhd1.read(0, 4) + oMediumIOhd1.close() + + oMediumIOhd2 = hd2.openForIO(True, "") + dataHd2 = oMediumIOhd2.read(0, 4) + oMediumIOhd2.close() + + if dataHd1 != dataHd2: + reporter.testFailure("Data read is unexpected.") + + if hd2.logicalSize not in (hd1.logicalSize, 1024*1024*2): + reporter.testFailure("Target medium did not resize.") + + self.deleteVM(oVM) + + reporter.testDone() + return True + + def testAll(self): + return self.testCloneOnly() & self.testResizeAndClone() + +if __name__ == '__main__': + sys.path.append(os.path.dirname(os.path.abspath(__file__))) + from tdApi1 import tdApi1; # pylint: disable=relative-import + sys.exit(tdApi1([SubTstDrvCloneMedium1]).main(sys.argv)) diff --git a/src/VBox/ValidationKit/tests/api/tdCreateVMWithDefaults1.py b/src/VBox/ValidationKit/tests/api/tdCreateVMWithDefaults1.py new file mode 100755 index 00000000..21892abb --- /dev/null +++ b/src/VBox/ValidationKit/tests/api/tdCreateVMWithDefaults1.py @@ -0,0 +1,203 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# $Id: tdCreateVMWithDefaults1.py $ + +""" +VirtualBox Validation Kit - Create VM with IMachine::applyDefaults() Test +""" + +__copyright__ = \ +""" +Copyright (C) 2010-2023 Oracle and/or its affiliates. + +This file is part of VirtualBox base platform packages, as +available from https://www.virtualbox.org. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation, in version 3 of the +License. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, see <https://www.gnu.org/licenses>. + +The contents of this file may alternatively be used under the terms +of the Common Development and Distribution License Version 1.0 +(CDDL), a copy of it is provided in the "COPYING.CDDL" file included +in the VirtualBox distribution, in which case the provisions of the +CDDL are applicable instead of those of the GPL. + +You may elect to license modified versions of this file under the +terms and conditions of either the GPL or the CDDL or both. + +SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +""" +__version__ = "$Revision: 155244 $" + + +# Standard Python imports. +import os +import sys + +# Only the main script needs to modify the path. +try: __file__ +except: __file__ = sys.argv[0] +g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +sys.path.append(g_ksValidationKitDir) + +# Validation Kit imports. +from testdriver import base +from testdriver import reporter; +from testdriver import vboxcon; + + +class SubTstDrvCreateVMWithDefaults1(base.SubTestDriverBase): + """ + Sub-test driver for VM Move Test #1. + """ + + def __init__(self, oTstDrv): + base.SubTestDriverBase.__init__(self, oTstDrv, 'create-vm-with-defaults', 'Create VMs with defaults'); + + def testIt(self): + """ + Execute the sub-testcase. + """ + reporter.log('ValidationKit folder is "%s"' % (g_ksValidationKitDir,)) + reporter.testStart(self.sTestName); + fRc = self.testCreateVMWithDefaults(); + reporter.testDone(); + return fRc; + + + def createVMWithDefaults(self, sGuestType): + sName = 'testvm_%s' % (sGuestType) + # create VM manually, because the self.createTestVM() makes registration inside + # the method, but IMachine::applyDefault() must be called before machine is registered + try: + if self.oTstDrv.fpApiVer >= 4.2: # Introduces grouping (third parameter, empty for now). + oVM = self.oTstDrv.oVBox.createMachine("", sName, [], + self.oTstDrv.tryFindGuestOsId(sGuestType), + "") + elif self.oTstDrv.fpApiVer >= 4.0: + oVM = self.oTstDrv.oVBox.createMachine("", sName, + self.oTstDrv.tryFindGuestOsId(sGuestType), + "", False) + elif self.oTstDrv.fpApiVer >= 3.2: + oVM = self.oTstDrv.oVBox.createMachine(sName, + self.oTstDrv.tryFindGuestOsId(sGuestType), + "", "", False) + else: + oVM = self.oTstDrv.oVBox.createMachine(sName, + self.oTstDrv.tryFindGuestOsId(sGuestType), + "", "") + try: + oVM.saveSettings() + except: + reporter.logXcpt() + if self.oTstDrv.fpApiVer >= 4.0: + try: + if self.oTstDrv.fpApiVer >= 4.3: + oProgress = oVM.deleteConfig([]) + else: + oProgress = oVM.delete(None); + self.oTstDrv.waitOnProgress(oProgress) + except: + reporter.logXcpt() + else: + try: oVM.deleteSettings() + except: reporter.logXcpt() + raise + except: + reporter.errorXcpt('failed to create vm "%s"' % (sName)) + return None + + if oVM is None: + return False + + # apply settings + fRc = True + try: + if self.oTstDrv.fpApiVer >= 6.1: + oVM.applyDefaults('') + oVM.saveSettings(); + self.oTstDrv.oVBox.registerMachine(oVM) + except: + reporter.logXcpt() + fRc = False + + # Some errors from applyDefaults can be observed only after further settings saving. + # Change and save the size of the VM RAM as simple setting change. + oSession = self.oTstDrv.openSession(oVM) + if oSession is None: + fRc = False + + if fRc: + try: + oSession.memorySize = 4096 + oSession.saveSettings(True) + except: + reporter.logXcpt() + fRc = False + + # delete VM + try: + oVM.unregister(vboxcon.CleanupMode_DetachAllReturnNone) + except: + reporter.logXcpt() + + if self.oTstDrv.fpApiVer >= 4.0: + try: + if self.oTstDrv.fpApiVer >= 4.3: + oProgress = oVM.deleteConfig([]) + else: + oProgress = oVM.delete(None) + self.oTstDrv.waitOnProgress(oProgress) + + except: + reporter.logXcpt() + + else: + try: oVM.deleteSettings() + except: reporter.logXcpt() + + return fRc + + def testCreateVMWithDefaults(self): + """ + Test create VM with defaults. + """ + if not self.oTstDrv.importVBoxApi(): + return reporter.error('importVBoxApi'); + + # Get the guest OS types. + try: + aoGuestTypes = self.oTstDrv.oVBoxMgr.getArray(self.oTstDrv.oVBox, 'guestOSTypes') + if aoGuestTypes is None or not aoGuestTypes: + return reporter.error('No guest OS types'); + except: + return reporter.errorXcpt(); + + # Create VMs with defaults for each of the guest types. + fRc = True + for oGuestType in aoGuestTypes: + try: + sGuestType = oGuestType.id; + except: + fRc = reporter.errorXcpt(); + else: + reporter.testStart(sGuestType); + fRc = self.createVMWithDefaults(sGuestType) and fRc; + reporter.testDone(); + return fRc + +if __name__ == '__main__': + sys.path.append(os.path.dirname(os.path.abspath(__file__))) + from tdApi1 import tdApi1; # pylint: disable=relative-import + sys.exit(tdApi1([SubTstDrvCreateVMWithDefaults1]).main(sys.argv)) + diff --git a/src/VBox/ValidationKit/tests/api/tdMoveMedium1.py b/src/VBox/ValidationKit/tests/api/tdMoveMedium1.py new file mode 100755 index 00000000..ca3f3891 --- /dev/null +++ b/src/VBox/ValidationKit/tests/api/tdMoveMedium1.py @@ -0,0 +1,217 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# $Id: tdMoveMedium1.py $ + +""" +VirtualBox Validation Kit - Medium Move Test #1 +""" + +__copyright__ = \ +""" +Copyright (C) 2010-2023 Oracle and/or its affiliates. + +This file is part of VirtualBox base platform packages, as +available from https://www.virtualbox.org. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation, in version 3 of the +License. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, see <https://www.gnu.org/licenses>. + +The contents of this file may alternatively be used under the terms +of the Common Development and Distribution License Version 1.0 +(CDDL), a copy of it is provided in the "COPYING.CDDL" file included +in the VirtualBox distribution, in which case the provisions of the +CDDL are applicable instead of those of the GPL. + +You may elect to license modified versions of this file under the +terms and conditions of either the GPL or the CDDL or both. + +SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +""" +__version__ = "$Revision: 155244 $" + + +# Standard Python imports. +import os +import sys + +# Only the main script needs to modify the path. +try: __file__ +except: __file__ = sys.argv[0] +g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +sys.path.append(g_ksValidationKitDir) + +# Validation Kit imports. +from testdriver import base +from testdriver import reporter +from testdriver import vboxcon +from testdriver import vboxwrappers + + +class SubTstDrvMoveMedium1(base.SubTestDriverBase): + """ + Sub-test driver for Medium Move Test #1. + """ + + def __init__(self, oTstDrv): + base.SubTestDriverBase.__init__(self, oTstDrv, 'move-medium', 'Move Medium'); + + def testIt(self): + """ + Execute the sub-testcase. + """ + return self.testMediumMove() + + # + # Test execution helpers. + # + + def moveTo(self, sLocation, aoMediumAttachments): + for oAttachment in aoMediumAttachments: + try: + oMedium = oAttachment.medium + reporter.log('Move medium "%s" to "%s"' % (oMedium.name, sLocation,)) + except: + reporter.errorXcpt('failed to get the medium from the IMediumAttachment "%s"' % (oAttachment)) + + if self.oTstDrv.fpApiVer >= 5.3 and self.oTstDrv.uRevision > 124748: + try: + oProgress = vboxwrappers.ProgressWrapper(oMedium.moveTo(sLocation), self.oTstDrv.oVBoxMgr, self.oTstDrv, + 'move "%s"' % (oMedium.name,)); + except: + return reporter.errorXcpt('Medium::moveTo("%s") for medium "%s" failed' % (sLocation, oMedium.name,)); + else: + try: + oProgress = vboxwrappers.ProgressWrapper(oMedium.setLocation(sLocation), self.oTstDrv.oVBoxMgr, self.oTstDrv, + 'move "%s"' % (oMedium.name,)); + except: + return reporter.errorXcpt('Medium::setLocation("%s") for medium "%s" failed' % (sLocation, oMedium.name,)); + + + oProgress.wait() + if oProgress.logResult() is False: + return False + return True + + def checkLocation(self, sLocation, aoMediumAttachments, asFiles): + fRc = True + for oAttachment in aoMediumAttachments: + sFilePath = os.path.join(sLocation, asFiles[oAttachment.port]) + sActualFilePath = oAttachment.medium.location + if os.path.abspath(sFilePath) != os.path.abspath(sActualFilePath): + reporter.log('medium location expected to be "%s" but is "%s"' % (sFilePath, sActualFilePath)) + fRc = False; + if not os.path.exists(sFilePath): + reporter.log('medium file does not exist at "%s"' % (sFilePath,)) + fRc = False; + return fRc + + def testMediumMove(self): + """ + Test medium moving. + """ + reporter.testStart('medium moving') + + try: ## @todo r=bird: Bad 'ing style. + oVM = self.oTstDrv.createTestVM('test-medium-move', 1, None, 4) + assert oVM is not None + + # create hard disk images, one for each file-based backend, using the first applicable extension + fRc = True + oSession = self.oTstDrv.openSession(oVM) + aoDskFmts = self.oTstDrv.oVBoxMgr.getArray(self.oTstDrv.oVBox.systemProperties, 'mediumFormats') + asFiles = [] + for oDskFmt in aoDskFmts: + aoDskFmtCaps = self.oTstDrv.oVBoxMgr.getArray(oDskFmt, 'capabilities') + if vboxcon.MediumFormatCapabilities_File not in aoDskFmtCaps \ + or vboxcon.MediumFormatCapabilities_CreateDynamic not in aoDskFmtCaps: + continue + (asExts, aTypes) = oDskFmt.describeFileExtensions() + for i in range(0, len(asExts)): #pylint: disable=consider-using-enumerate + if aTypes[i] is vboxcon.DeviceType_HardDisk: + sExt = '.' + asExts[i] + break + if sExt is None: + fRc = False + break + sFile = 'Test' + str(len(asFiles)) + sExt + sHddPath = os.path.join(self.oTstDrv.sScratchPath, sFile) + oHd = oSession.createBaseHd(sHddPath, sFmt=oDskFmt.id, cb=1024*1024) + if oHd is None: + fRc = False + break + + # attach HDD, IDE controller exists by default, but we use SATA just in case + sController='SATA Controller' + fRc = fRc and oSession.attachHd(sHddPath, sController, iPort = len(asFiles), + fImmutable=False, fForceResource=False) + if fRc: + asFiles.append(sFile) + + fRc = fRc and oSession.saveSettings() + + #create temporary subdirectory in the current working directory + sOrigLoc = self.oTstDrv.sScratchPath + sNewLoc = os.path.join(sOrigLoc, 'newLocation') + os.mkdir(sNewLoc, 0o775) + + aoMediumAttachments = oVM.getMediumAttachmentsOfController(sController) + #case 1. Only path without file name, with trailing separator + fRc = self.moveTo(sNewLoc + os.sep, aoMediumAttachments) and fRc + fRc = self.checkLocation(sNewLoc, aoMediumAttachments, asFiles) and fRc + + #case 2. Only path without file name, without trailing separator + fRc = self.moveTo(sOrigLoc, aoMediumAttachments) and fRc + fRc = self.checkLocation(sOrigLoc, aoMediumAttachments, asFiles) and fRc + + #case 3. Path with file name + #The case supposes that user has passed a destination path with a file name but hasn't added an extension/suffix + #to this destination file. User supposes that the extension would be added automatically and to be the same as + #for the original file. Difficult case, apparently this case should follow mv(1) logic + #and the file name is processed as folder name (aka mv(1) logic). + #Be discussed. + fRc = self.moveTo(os.path.join(sNewLoc, 'newName'), aoMediumAttachments) and fRc + asNewFiles = ['newName' + os.path.splitext(s)[1] for s in asFiles] + fRc = self.checkLocation(os.path.join(sNewLoc, 'newName'), aoMediumAttachments, asFiles) and fRc + + #after the case the destination path must be corrected + sNewLoc = os.path.join(sNewLoc, 'newName') + + #case 4. Only file name + fRc = self.moveTo('onlyMediumName', aoMediumAttachments) and fRc + asNewFiles = ['onlyMediumName' + os.path.splitext(s)[1] for s in asFiles] + if self.oTstDrv.fpApiVer >= 5.3: + fRc = self.checkLocation(sNewLoc, aoMediumAttachments, asNewFiles) and fRc + else: + fRc = self.checkLocation(sNewLoc, aoMediumAttachments, + [s.replace('.hdd', '.parallels') for s in asNewFiles]) and fRc + + #case 5. Move all files from a snapshot + fRc = fRc and oSession.takeSnapshot('Snapshot1') + if fRc: + aoMediumAttachments = oVM.getMediumAttachmentsOfController(sController) + asSnapFiles = [os.path.basename(o.medium.name) for o in aoMediumAttachments] + fRc = self.moveTo(sOrigLoc, aoMediumAttachments) and fRc + fRc = self.checkLocation(sOrigLoc, aoMediumAttachments, asSnapFiles) and fRc + + fRc = oSession.close() and fRc + except: + reporter.errorXcpt() + + return reporter.testDone()[1] == 0 + + +if __name__ == '__main__': + sys.path.append(os.path.dirname(os.path.abspath(__file__))) + from tdApi1 import tdApi1; # pylint: disable=relative-import + sys.exit(tdApi1([SubTstDrvMoveMedium1]).main(sys.argv)) + diff --git a/src/VBox/ValidationKit/tests/api/tdMoveVm1.py b/src/VBox/ValidationKit/tests/api/tdMoveVm1.py new file mode 100755 index 00000000..3bf058d5 --- /dev/null +++ b/src/VBox/ValidationKit/tests/api/tdMoveVm1.py @@ -0,0 +1,768 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# "$Id: tdMoveVm1.py $" + +""" +VirtualBox Validation Kit - VM Move Test #1 +""" + +__copyright__ = \ +""" +Copyright (C) 2010-2023 Oracle and/or its affiliates. + +This file is part of VirtualBox base platform packages, as +available from https://www.virtualbox.org. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation, in version 3 of the +License. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, see <https://www.gnu.org/licenses>. + +The contents of this file may alternatively be used under the terms +of the Common Development and Distribution License Version 1.0 +(CDDL), a copy of it is provided in the "COPYING.CDDL" file included +in the VirtualBox distribution, in which case the provisions of the +CDDL are applicable instead of those of the GPL. + +You may elect to license modified versions of this file under the +terms and conditions of either the GPL or the CDDL or both. + +SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +""" +__version__ = "$Revision: 155244 $" + +# Standard Python imports. +import os +import sys +import shutil +from collections import defaultdict + +# Only the main script needs to modify the path. +try: __file__ +except: __file__ = sys.argv[0] +g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +sys.path.append(g_ksValidationKitDir) + +# Validation Kit imports. +from testdriver import base +from testdriver import reporter +from testdriver import vboxcon +from testdriver import vboxwrappers +from tdMoveMedium1 import SubTstDrvMoveMedium1; # pylint: disable=relative-import + + +# @todo r=aeichner: The whole path handling/checking needs fixing to also work on Windows +# The current quick workaround is to spill os.path.normcase() all over the place when +# constructing paths. I'll leave the real fix to the original author... +class SubTstDrvMoveVm1(base.SubTestDriverBase): + """ + Sub-test driver for VM Move Test #1. + """ + + def __init__(self, oTstDrv): + base.SubTestDriverBase.__init__(self, oTstDrv, 'move-vm', 'Move VM'); + + # Note! Hardcoded indexing in test code further down. + self.asRsrcs = [ + os.path.join('5.3','isos','tdMoveVM1.iso'), + os.path.join('5.3','floppy','tdMoveVM1.img') + ]; + + self.asImagesNames = [] + self.dsKeys = { + 'StandardImage': 'SATA Controller', + 'ISOImage': 'IDE Controller', + 'FloppyImage': 'Floppy Controller', + 'SettingsFile': 'Settings File', + 'LogFile': 'Log File', + 'SavedStateFile': 'Saved State File', + 'SnapshotFile': 'Snapshot File' + }; + + def testIt(self): + """ + Execute the sub-testcase. + """ + reporter.testStart(self.sTestName); + reporter.log('ValidationKit folder is "%s"' % (g_ksValidationKitDir,)) + fRc = self.testVMMove(); + return reporter.testDone(fRc is None)[1] == 0; + + # + # Test execution helpers. + # + + def createTestMachine(self): + """ + Document me here, not with hashes above. + """ + oVM = self.oTstDrv.createTestVM('test-vm-move', 1, None, 4) + if oVM is None: + return None + + # create hard disk images, one for each file-based backend, using the first applicable extension + fRc = True + oSession = self.oTstDrv.openSession(oVM) + aoDskFmts = self.oTstDrv.oVBoxMgr.getArray(self.oTstDrv.oVBox.systemProperties, 'mediumFormats') + + for oDskFmt in aoDskFmts: + aoDskFmtCaps = self.oTstDrv.oVBoxMgr.getArray(oDskFmt, 'capabilities') + if vboxcon.MediumFormatCapabilities_File not in aoDskFmtCaps \ + or vboxcon.MediumFormatCapabilities_CreateDynamic not in aoDskFmtCaps: + continue + (asExts, aTypes) = oDskFmt.describeFileExtensions() + for i in range(0, len(asExts)): # pylint: disable=consider-using-enumerate + if aTypes[i] is vboxcon.DeviceType_HardDisk: + sExt = '.' + asExts[i] + break + if sExt is None: + fRc = False + break + sFile = 'test-vm-move' + str(len(self.asImagesNames)) + sExt + sHddPath = os.path.join(self.oTstDrv.sScratchPath, sFile) + oHd = oSession.createBaseHd(sHddPath, sFmt=oDskFmt.id, cb=1024*1024) + if oHd is None: + fRc = False + break + + # attach HDD, IDE controller exists by default, but we use SATA just in case + sController = self.dsKeys['StandardImage'] + fRc = fRc and oSession.attachHd(sHddPath, sController, iPort = len(self.asImagesNames), + fImmutable=False, fForceResource=False) + if fRc: + self.asImagesNames.append(sFile) + + fRc = fRc and oSession.saveSettings() + fRc = oSession.close() and fRc + + if fRc is False: + oVM = None + + return oVM + + def moveVMToLocation(self, sLocation, oVM): + """ + Document me here, not with hashes above. + """ + fRc = True + try: + + ## @todo r=bird: Too much unncessary crap inside try clause. Only oVM.moveTo needs to be here. + ## Though, you could make an argument for oVM.name too, perhaps. + + # move machine + reporter.log('Moving machine "%s" to the "%s"' % (oVM.name, sLocation)) + sType = 'basic' + oProgress = vboxwrappers.ProgressWrapper(oVM.moveTo(sLocation, sType), self.oTstDrv.oVBoxMgr, self.oTstDrv, + 'moving machine "%s"' % (oVM.name,)) + + except: + return reporter.errorXcpt('Machine::moveTo("%s") for machine "%s" failed' % (sLocation, oVM.name,)) + + oProgress.wait() + if oProgress.logResult() is False: + fRc = False + reporter.log('Progress object returned False') + else: + fRc = True + + return fRc + + def checkLocation(self, oMachine, dsReferenceFiles): + """ + Document me. + + Prerequisites: + 1. All standard images are attached to SATA controller + 2. All ISO images are attached to IDE controller + 3. All floppy images are attached to Floppy controller + 4. The type defaultdict from collection is used here (some sort of multimap data structure) + 5. The dsReferenceFiles parameter here is the structure defaultdict(set): + [ + ('StandardImage': ['somedisk.vdi', 'somedisk.vmdk',...]), + ('ISOImage': ['somedisk_1.iso','somedisk_2.iso',...]), + ('FloppyImage': ['somedisk_1.img','somedisk_2.img',...]), + ('SnapshotFile': ['snapshot file 1','snapshot file 2', ...]), + ('SettingsFile', ['setting file',...]), + ('SavedStateFile': ['state file 1','state file 2',...]), + ('LogFile': ['log file 1','log file 2',...]), + ] + """ + + fRc = True + + for sKey, sValue in self.dsKeys.items(): + aActuals = set() + aReferences = set() + + # Check standard images locations, ISO files locations, floppy images locations, snapshots files locations + if sKey in ('StandardImage', 'ISOImage', 'FloppyImage',): + aReferences = dsReferenceFiles[sKey] + if aReferences: + aoMediumAttachments = oMachine.getMediumAttachmentsOfController(sValue) ##@todo r=bird: API call, try-except! + for oAttachment in aoMediumAttachments: + aActuals.add(os.path.normcase(oAttachment.medium.location)) + + elif sKey == 'SnapshotFile': + aReferences = dsReferenceFiles[sKey] + if aReferences: + aActuals = self.__getSnapshotsFiles(oMachine) + + # Check setting file location + elif sKey == 'SettingsFile': + aReferences = dsReferenceFiles[sKey] + if aReferences: + aActuals.add(os.path.normcase(oMachine.settingsFilePath)) + + # Check saved state files location + elif sKey == 'SavedStateFile': + aReferences = dsReferenceFiles[sKey] + if aReferences: + aActuals = self.__getStatesFiles(oMachine) + + # Check log files location + elif sKey == 'LogFile': + aReferences = dsReferenceFiles[sKey] + if aReferences: + aActuals = self.__getLogFiles(oMachine) + + if aActuals: + reporter.log('Check %s' % (sKey)) + intersection = aReferences.intersection(aActuals) + for eachItem in intersection: + reporter.log('Item location "%s" is correct' % (eachItem)) + + difference = aReferences.difference(aActuals) + for eachItem in difference: + reporter.log('Item location "%s" isn\'t correct' % (eachItem)) + + reporter.log('####### Reference locations: #######') + for eachItem in aActuals: + reporter.log(' "%s"' % (eachItem)) + + if len(intersection) != len(aActuals): + reporter.log('Not all items in the right location. Check it.') + fRc = False + + return fRc + + def checkAPIVersion(self): + return self.oTstDrv.fpApiVer >= 5.3; + + @staticmethod + def __safeListDir(sDir): + """ Wrapper around os.listdir that returns empty array instead of exceptions. """ + try: + return os.listdir(sDir); + except: + return []; + + def __getStatesFiles(self, oMachine, fPrint = False): + asStateFilesList = set() + sFolder = oMachine.snapshotFolder; + for sFile in self.__safeListDir(sFolder): + if sFile.endswith(".sav"): + sFullPath = os.path.normcase(os.path.join(sFolder, sFile)); + asStateFilesList.add(sFullPath) + if fPrint is True: + reporter.log("State file is %s" % (sFullPath)) + return asStateFilesList + + def __getSnapshotsFiles(self, oMachine, fPrint = False): + asSnapshotsFilesList = set() + sFolder = oMachine.snapshotFolder + for sFile in self.__safeListDir(sFolder): + if sFile.endswith(".sav") is False: + sFullPath = os.path.normcase(os.path.join(sFolder, sFile)); + asSnapshotsFilesList.add(sFullPath) + if fPrint is True: + reporter.log("Snapshot file is %s" % (sFullPath)) + return asSnapshotsFilesList + + def __getLogFiles(self, oMachine, fPrint = False): + asLogFilesList = set() + sFolder = oMachine.logFolder + for sFile in self.__safeListDir(sFolder): + if sFile.endswith(".log"): + sFullPath = os.path.normcase(os.path.join(sFolder, sFile)); + asLogFilesList.add(sFullPath) + if fPrint is True: + reporter.log("Log file is %s" % (sFullPath)) + return asLogFilesList + + + def __testScenario_2(self, oSession, oMachine, sNewLoc, sOldLoc): + """ + All disks attached to VM are located inside the VM's folder. + There are no any snapshots and logs. + """ + + sController = self.dsKeys['StandardImage'] + aoMediumAttachments = oMachine.getMediumAttachmentsOfController(sController) + oSubTstDrvMoveMedium1Instance = SubTstDrvMoveMedium1(self.oTstDrv) + oSubTstDrvMoveMedium1Instance.moveTo(sOldLoc, aoMediumAttachments) + + del oSubTstDrvMoveMedium1Instance + + dsReferenceFiles = defaultdict(set) + + for s in self.asImagesNames: + reporter.log('"%s"' % (s,)) + dsReferenceFiles['StandardImage'].add(os.path.normcase(sNewLoc + os.sep + oMachine.name + os.sep + s)) + + sSettingFile = os.path.join(sNewLoc, os.path.join(oMachine.name, oMachine.name + '.vbox')) + dsReferenceFiles['SettingsFile'].add(os.path.normcase(sSettingFile)) + + fRc = self.moveVMToLocation(sNewLoc, oSession.o.machine) + + if fRc is True: + fRc = self.checkLocation(oSession.o.machine, dsReferenceFiles) + if fRc is False: + reporter.testFailure('!!!!!!!!!!!!!!!!!! 2nd scenario: Check locations failed... !!!!!!!!!!!!!!!!!!') + else: + reporter.testFailure('!!!!!!!!!!!!!!!!!! 2nd scenario: Move VM failed... !!!!!!!!!!!!!!!!!!') + + fRes = oSession.saveSettings() + if fRes is False: + reporter.log('2nd scenario: Couldn\'t save machine settings') + + return fRc + + def __testScenario_3(self, oSession, oMachine, sNewLoc): + """ + There are snapshots + """ + + # At moment, it's used only one snapshot due to the difficulty to get + # all attachments of the machine (i.e. not only attached at moment) + cSnap = 1 + + for counter in range(1,cSnap+1): + strSnapshot = 'Snapshot' + str(counter) + fRc = oSession.takeSnapshot(strSnapshot) + if fRc is False: + reporter.testFailure('3rd scenario: Can\'t take snapshot "%s"' % (strSnapshot,)) + + dsReferenceFiles = defaultdict(set) + + sController = self.dsKeys['StandardImage'] + aoMediumAttachments = oMachine.getMediumAttachmentsOfController(sController) + if fRc is True: + for oAttachment in aoMediumAttachments: + sRes = oAttachment.medium.location.rpartition(os.sep) + dsReferenceFiles['SnapshotFile'].add(os.path.normcase(sNewLoc + os.sep + oMachine.name + os.sep + + 'Snapshots' + os.sep + sRes[2])) + + sSettingFile = os.path.join(sNewLoc, os.path.join(oMachine.name, oMachine.name + '.vbox')) + dsReferenceFiles['SettingsFile'].add(os.path.normcase(sSettingFile)) + + fRc = self.moveVMToLocation(sNewLoc, oSession.o.machine) + + if fRc is True: + fRc = self.checkLocation(oSession.o.machine, dsReferenceFiles) + if fRc is False: + reporter.testFailure('!!!!!!!!!!!!!!!!!! 3rd scenario: Check locations failed... !!!!!!!!!!!!!!!!!!') + else: + reporter.testFailure('!!!!!!!!!!!!!!!!!! 3rd scenario: Move VM failed... !!!!!!!!!!!!!!!!!!') + + fRes = oSession.saveSettings() + if fRes is False: + reporter.log('3rd scenario: Couldn\'t save machine settings') + + return fRc + + def __testScenario_4(self, oMachine, sNewLoc): + """ + There are one or more save state files in the snapshots folder + and some files in the logs folder. + Here we run VM, next stop it in the "save" state. + And next move VM + """ + + # Run VM and get new Session object. + oSession = self.oTstDrv.startVm(oMachine); + if not oSession: + return False; + + # Some time interval should be here for not closing VM just after start. + self.oTstDrv.waitForTasks(1000); + + if oMachine.state != self.oTstDrv.oVBoxMgr.constants.MachineState_Running: + reporter.log("Machine '%s' is not Running" % (oMachine.name)) + fRc = False + + # Call Session::saveState(), already closes session unless it failed. + fRc = oSession.saveState() + if fRc is True: + reporter.log("Machine is in saved state") + + fRc = self.oTstDrv.terminateVmBySession(oSession) + + if fRc is True: + # Create a new Session object for moving VM. + oSession = self.oTstDrv.openSession(oMachine) + + # Always clear before each scenario. + dsReferenceFiles = defaultdict(set) + + asLogs = self.__getLogFiles(oMachine) + for sFile in asLogs: + sRes = sFile.rpartition(os.sep) + dsReferenceFiles['LogFile'].add(os.path.normcase(sNewLoc + os.sep + oMachine.name + os.sep + + 'Logs' + os.sep + sRes[2])) + + asStates = self.__getStatesFiles(oMachine) + for sFile in asStates: + sRes = sFile.rpartition(os.sep) + dsReferenceFiles['SavedStateFile'].add(os.path.normcase(sNewLoc + os.sep + oMachine.name + os.sep + + 'Snapshots' + os.sep + sRes[2])) + + fRc = self.moveVMToLocation(sNewLoc, oSession.o.machine) + + if fRc is True: + fRc = self.checkLocation(oSession.o.machine, dsReferenceFiles) + if fRc is False: + reporter.testFailure('!!!!!!!!!!!!!!!!!! 4th scenario: Check locations failed... !!!!!!!!!!!!!!!!!!') + else: + reporter.testFailure('!!!!!!!!!!!!!!!!!! 4th scenario: Move VM failed... !!!!!!!!!!!!!!!!!!') + + # cleaning up: get rid of saved state + fRes = oSession.discardSavedState(True) + if fRes is False: + reporter.log('4th scenario: Failed to discard the saved state of machine') + + fRes = oSession.close() + if fRes is False: + reporter.log('4th scenario: Couldn\'t close machine session') + else: + reporter.testFailure('!!!!!!!!!!!!!!!!!! 4th scenario: Terminate machine by session failed... !!!!!!!!!!!!!!!!!!') + + return fRc + + def __testScenario_5(self, oMachine, sNewLoc, sOldLoc): + """ + There is an ISO image (.iso) attached to the VM. + Prerequisites - there is IDE Controller and there are no any images attached to it. + """ + + fRc = True + sISOImageName = 'tdMoveVM1.iso' + + # Always clear before each scenario. + dsReferenceFiles = defaultdict(set) + + # Create a new Session object. + oSession = self.oTstDrv.openSession(oMachine) + + sISOLoc = self.asRsrcs[0] # '5.3/isos/tdMoveVM1.iso' + reporter.log("sHost is '%s', sResourcePath is '%s'" % (self.oTstDrv.sHost, self.oTstDrv.sResourcePath)) + sISOLoc = self.oTstDrv.getFullResourceName(sISOLoc) + reporter.log("sISOLoc is '%s'" % (sISOLoc,)) + + if not os.path.exists(sISOLoc): + reporter.log('ISO file does not exist at "%s"' % (sISOLoc,)) + fRc = False + + # Copy ISO image from the common resource folder into machine folder. + shutil.copy(sISOLoc, sOldLoc) + + # Attach ISO image to the IDE controller. + if fRc is True: + # Set actual ISO location. + sISOLoc = sOldLoc + os.sep + sISOImageName + reporter.log("sISOLoc is '%s'" % (sISOLoc,)) + if not os.path.exists(sISOLoc): + reporter.log('ISO file does not exist at "%s"' % (sISOLoc,)) + fRc = False + + sController=self.dsKeys['ISOImage'] + aoMediumAttachments = oMachine.getMediumAttachmentsOfController(sController) + iPort = len(aoMediumAttachments) + fRc = oSession.attachDvd(sISOLoc, sController, iPort, iDevice = 0) + dsReferenceFiles['ISOImage'].add(os.path.normcase(os.path.join(os.path.join(sNewLoc, oMachine.name), sISOImageName))) + + if fRc is True: + fRc = self.moveVMToLocation(sNewLoc, oSession.o.machine) + if fRc is True: + fRc = self.checkLocation(oSession.o.machine, dsReferenceFiles) + if fRc is False: + reporter.testFailure('!!!!!!!!!!!!!!!!!! 5th scenario: Check locations failed... !!!!!!!!!!!!!!!!!!') + else: + reporter.testFailure('!!!!!!!!!!!!!!!!!! 5th scenario: Move VM failed... !!!!!!!!!!!!!!!!!!') + else: + reporter.testFailure('!!!!!!!!!!!!!!!!!! 5th scenario: Attach ISO image failed... !!!!!!!!!!!!!!!!!!') + + # Detach ISO image. + fRes = oSession.detachHd(sController, iPort, 0) + if fRes is False: + reporter.log('5th scenario: Couldn\'t detach image from the controller %s ' + 'port %s device %s' % (sController, iPort, 0)) + + fRes = oSession.saveSettings() + if fRes is False: + reporter.log('5th scenario: Couldn\'t save machine settings') + + fRes = oSession.close() + if fRes is False: + reporter.log('5th scenario: Couldn\'t close machine session') + + return fRc + + def __testScenario_6(self, oMachine, sNewLoc, sOldLoc): + """ + There is a floppy image (.img) attached to the VM. + Prerequisites - there is Floppy Controller and there are no any images attached to it. + """ + + fRc = True + + # Always clear before each scenario. + dsReferenceFiles = defaultdict(set) + + # Create a new Session object. + oSession = self.oTstDrv.openSession(oMachine) + + sFloppyLoc = self.asRsrcs[1] # '5.3/floppy/tdMoveVM1.img' + sFloppyLoc = self.oTstDrv.getFullResourceName(sFloppyLoc) + + if not os.path.exists(sFloppyLoc): + reporter.log('Floppy disk does not exist at "%s"' % (sFloppyLoc,)) + fRc = False + + # Copy floppy image from the common resource folder into machine folder. + shutil.copy(sFloppyLoc, sOldLoc) + + # Attach floppy image. + if fRc is True: + # Set actual floppy location. + sFloppyImageName = 'tdMoveVM1.img' + sFloppyLoc = sOldLoc + os.sep + sFloppyImageName + sController=self.dsKeys['FloppyImage'] + fRc = fRc and oSession.attachFloppy(sFloppyLoc, sController, 0, 0) + dsReferenceFiles['FloppyImage'].add(os.path.normcase(os.path.join(os.path.join(sNewLoc, oMachine.name), + sFloppyImageName))) + + if fRc is True: + fRc = self.moveVMToLocation(sNewLoc, oSession.o.machine) + if fRc is True: + fRc = self.checkLocation(oSession.o.machine, dsReferenceFiles) + if fRc is False: + reporter.testFailure('!!!!!!!!!!!!!!!!!! 6th scenario: Check locations failed... !!!!!!!!!!!!!!!!!!') + else: + reporter.testFailure('!!!!!!!!!!!!!!!!!! 6th scenario: Move VM failed... !!!!!!!!!!!!!!!!!!') + else: + reporter.testFailure('!!!!!!!!!!!!!!!!!! 6th scenario: Attach floppy image failed... !!!!!!!!!!!!!!!!!!') + + # Detach floppy image. + fRes = oSession.detachHd(sController, 0, 0) + if fRes is False: + reporter.log('6th scenario: Couldn\'t detach image from the controller %s port %s device %s' % (sController, 0, 0)) + + fRes = oSession.saveSettings() + if fRes is False: + reporter.testFailure('6th scenario: Couldn\'t save machine settings') + + fRes = oSession.close() + if fRes is False: + reporter.log('6th scenario: Couldn\'t close machine session') + return fRc + + + def testVMMove(self): + """ + Test machine moving. + """ + if not self.oTstDrv.importVBoxApi(): + return False + + fSupported = self.checkAPIVersion() + reporter.log('ValidationKit folder is "%s"' % (g_ksValidationKitDir,)) + + if fSupported is False: + reporter.log('API version %s is too old. Just skip this test.' % (self.oTstDrv.fpApiVer)) + return None; + reporter.log('API version is "%s".' % (self.oTstDrv.fpApiVer)) + + # Scenarios + # 1. All disks attached to VM are located outside the VM's folder. + # There are no any snapshots and logs. + # In this case only VM setting file should be moved (.vbox file) + # + # 2. All disks attached to VM are located inside the VM's folder. + # There are no any snapshots and logs. + # + # 3. There are snapshots. + # + # 4. There are one or more save state files in the snapshots folder + # and some files in the logs folder. + # + # 5. There is an ISO image (.iso) attached to the VM. + # + # 6. There is a floppy image (.img) attached to the VM. + # + # 7. There are shareable disk and immutable disk attached to the VM. + + try: ## @todo r=bird: Would be nice to use sub-tests here for each scenario, however + ## this try/catch as well as lots of return points makes that very hard. + ## Big try/catch stuff like this should be avoided. + # Create test machine. + oMachine = self.createTestMachine() + if oMachine is None: + reporter.error('Failed to create test machine') + + # Create temporary subdirectory in the current working directory. + sOrigLoc = self.oTstDrv.sScratchPath + sBaseLoc = os.path.join(sOrigLoc, 'moveFolder') + os.mkdir(sBaseLoc, 0o775) + + # lock machine + # get session machine + oSession = self.oTstDrv.openSession(oMachine) + fRc = True + + sNewLoc = sBaseLoc + os.sep + + dsReferenceFiles = defaultdict(set) + + # + # 1. case: + # + # All disks attached to VM are located outside the VM's folder. + # There are no any snapshots and logs. + # In this case only VM setting file should be moved (.vbox file) + # + reporter.log("Scenario #1:"); + for s in self.asImagesNames: + reporter.log('"%s"' % (s,)) + dsReferenceFiles['StandardImage'].add(os.path.normcase(os.path.join(sOrigLoc, s))) + + sSettingFile = os.path.normcase(os.path.join(sNewLoc, os.path.join(oMachine.name, oMachine.name + '.vbox'))) + dsReferenceFiles['SettingsFile'].add(sSettingFile) + + fRc = self.moveVMToLocation(sNewLoc, oSession.o.machine) + + if fRc is True: + fRc = self.checkLocation(oSession.o.machine, dsReferenceFiles) + if fRc is False: + reporter.testFailure('!!!!!!!!!!!!!!!!!! 1st scenario: Check locations failed... !!!!!!!!!!!!!!!!!!') + return False; + else: + reporter.testFailure('!!!!!!!!!!!!!!!!!! 1st scenario: Move VM failed... !!!!!!!!!!!!!!!!!!') + return False; + + fRc = oSession.saveSettings() + if fRc is False: + reporter.testFailure('1st scenario: Couldn\'t save machine settings') + + # + # 2. case: + # + # All disks attached to VM are located inside the VM's folder. + # There are no any snapshots and logs. + # + reporter.log("Scenario #2:"); + sOldLoc = sNewLoc + oMachine.name + os.sep + sNewLoc = os.path.join(sOrigLoc, 'moveFolder_2nd_scenario') + os.mkdir(sNewLoc, 0o775) + + fRc = self.__testScenario_2(oSession, oMachine, sNewLoc, sOldLoc) + if fRc is False: + return False; + + # + # 3. case: + # + # There are snapshots. + # + reporter.log("Scenario #3:"); + sOldLoc = sNewLoc + oMachine.name + os.sep + sNewLoc = os.path.join(sOrigLoc, 'moveFolder_3rd_scenario') + os.mkdir(sNewLoc, 0o775) + + fRc = self.__testScenario_3(oSession, oMachine, sNewLoc) + if fRc is False: + return False; + + # + # 4. case: + # + # There are one or more save state files in the snapshots folder + # and some files in the logs folder. + # Here we run VM, next stop it in the "save" state. + # And next move VM + # + reporter.log("Scenario #4:"); + sOldLoc = sNewLoc + oMachine.name + os.sep + sNewLoc = os.path.join(sOrigLoc, 'moveFolder_4th_scenario') + os.mkdir(sNewLoc, 0o775) + + # Close Session object because after starting VM we get new instance of session + fRc = oSession.close() and fRc + if fRc is False: + reporter.log('Couldn\'t close machine session') + + del oSession + + fRc = self.__testScenario_4(oMachine, sNewLoc) + if fRc is False: + return False; + + # + # 5. case: + # + # There is an ISO image (.iso) attached to the VM. + # Prerequisites - there is IDE Controller and there are no any images attached to it. + # + reporter.log("Scenario #5:"); + sOldLoc = sNewLoc + os.sep + oMachine.name + sNewLoc = os.path.join(sOrigLoc, 'moveFolder_5th_scenario') + os.mkdir(sNewLoc, 0o775) + fRc = self.__testScenario_5(oMachine, sNewLoc, sOldLoc) + if fRc is False: + return False; + + # + # 6. case: + # + # There is a floppy image (.img) attached to the VM. + # Prerequisites - there is Floppy Controller and there are no any images attached to it. + # + reporter.log("Scenario #6:"); + sOldLoc = sNewLoc + os.sep + oMachine.name + sNewLoc = os.path.join(sOrigLoc, 'moveFolder_6th_scenario') + os.mkdir(sNewLoc, 0o775) + fRc = self.__testScenario_6(oMachine, sNewLoc, sOldLoc) + if fRc is False: + return False; + +# # +# # 7. case: +# # +# # There are shareable disk and immutable disk attached to the VM. +# # +# reporter.log("Scenario #7:"); +# fRc = fRc and oSession.saveSettings() +# if fRc is False: +# reporter.log('Couldn\'t save machine settings') +# + + assert fRc is True + except: + reporter.errorXcpt() + + return fRc; + + +if __name__ == '__main__': + sys.path.append(os.path.dirname(os.path.abspath(__file__))) + from tdApi1 import tdApi1; # pylint: disable=relative-import + sys.exit(tdApi1([SubTstDrvMoveVm1]).main(sys.argv)) + diff --git a/src/VBox/ValidationKit/tests/api/tdPython1.py b/src/VBox/ValidationKit/tests/api/tdPython1.py new file mode 100755 index 00000000..dd9dffc7 --- /dev/null +++ b/src/VBox/ValidationKit/tests/api/tdPython1.py @@ -0,0 +1,220 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# $Id: tdPython1.py $ + +""" +VirtualBox Validation Kit - Python Bindings Test #1 +""" + +__copyright__ = \ +""" +Copyright (C) 2010-2023 Oracle and/or its affiliates. + +This file is part of VirtualBox base platform packages, as +available from https://www.virtualbox.org. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation, in version 3 of the +License. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, see <https://www.gnu.org/licenses>. + +The contents of this file may alternatively be used under the terms +of the Common Development and Distribution License Version 1.0 +(CDDL), a copy of it is provided in the "COPYING.CDDL" file included +in the VirtualBox distribution, in which case the provisions of the +CDDL are applicable instead of those of the GPL. + +You may elect to license modified versions of this file under the +terms and conditions of either the GPL or the CDDL or both. + +SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +""" +__version__ = "$Revision: 155244 $" + + +# Standard Python imports. +import os +import sys +import time +import threading + +# Only the main script needs to modify the path. +try: __file__ +except: __file__ = sys.argv[0]; +g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))); +sys.path.append(g_ksValidationKitDir); + +# Validation Kit imports. +from testdriver import base; +from testdriver import reporter; + + +class SubTstDrvPython1(base.SubTestDriverBase): + """ + Sub-test driver for Python Bindings Test #1. + """ + + def __init__(self, oTstDrv): + base.SubTestDriverBase.__init__(self, oTstDrv, 'python-binding', 'Python bindings'); + + def testIt(self): + """ + Execute the sub-testcase. + """ + return self.testEventQueueWaiting() \ + and self.testEventQueueInterrupt(); + + # + # Test execution helpers. + # + + def testEventQueueWaitingThreadProc(self): + """ Thread procedure for checking that waitForEvents fails when not called by the main thread. """ + try: + rc2 = self.oTstDrv.oVBoxMgr.waitForEvents(0); + except: + return True; + reporter.error('waitForEvents() returned "%s" when called on a worker thread, expected exception.' % (rc2,)); + return False; + + def testEventQueueWaiting(self): + """ + Test event queue waiting. + """ + reporter.testStart('waitForEvents'); + + # Check return values and such. + for cMsTimeout in (0, 1, 2, 3, 256, 1000, 0): + iLoop = 0; + while True: + try: + rc = self.oTstDrv.oVBoxMgr.waitForEvents(cMsTimeout); + except: + reporter.errorXcpt(); + break; + if not isinstance(rc, int): + reporter.error('waitForEvents returns non-integer type'); + break; + if rc == 1: + break; + if rc != 0: + reporter.error('waitForEvents returns "%s", expected 0 or 1' % (rc,)); + break; + iLoop += 1; + if iLoop > 10240: + reporter.error('waitForEvents returns 0 (success) %u times. ' + 'Expected 1 (timeout/interrupt) after a call or two.' + % (iLoop,)); + break; + if reporter.testErrorCount() != 0: + break; + + # Check that we get an exception when trying to call the method from + # a different thread. + reporter.log('If running a debug build, you will see an ignored assertion now. Please ignore it.') + sVBoxAssertSaved = os.environ.get('VBOX_ASSERT', 'breakpoint'); + os.environ['VBOX_ASSERT'] = 'ignore'; + oThread = threading.Thread(target=self.testEventQueueWaitingThreadProc); + oThread.start(); + oThread.join(); + os.environ['VBOX_ASSERT'] = sVBoxAssertSaved; + + return reporter.testDone()[1] == 0; + + def interruptWaitEventsThreadProc(self): + """ Thread procedure that's used for waking up the main thread. """ + time.sleep(2); + try: + rc2 = self.oTstDrv.oVBoxMgr.interruptWaitEvents(); + except: + reporter.errorXcpt(); + else: + if rc2 is True: + return True; + reporter.error('interruptWaitEvents returned "%s" when called from other thread, expected True' % (rc2,)); + return False; + + def testEventQueueInterrupt(self): + """ + Test interrupting an event queue wait. + """ + reporter.testStart('interruptWait'); + + # interrupt ourselves first and check the return value. + for i in range(0, 10): + try: + rc = self.oTstDrv.oVBoxMgr.interruptWaitEvents(); + except: + reporter.errorXcpt(); + break; + if rc is not True: + reporter.error('interruptWaitEvents returned "%s" expected True' % (rc,)); + break + + if reporter.testErrorCount() == 0: + # + # Interrupt a waitForEvents call. + # + # This test ASSUMES that no other events are posted to the thread's + # event queue once we've drained it. Also ASSUMES the box is + # relatively fast and not too busy because we're timing sensitive. + # + for i in range(0, 4): + # Try quiesce the event queue. + for _ in range(1, 100): + self.oTstDrv.oVBoxMgr.waitForEvents(0); + + # Create a thread that will interrupt us in 2 seconds. + try: + oThread = threading.Thread(target=self.interruptWaitEventsThreadProc); + oThread.setDaemon(False); # pylint: disable=deprecated-method + except: + reporter.errorXcpt(); + break; + + cMsTimeout = 20000; + if i == 2: + cMsTimeout = -1; + elif i == 3: + cMsTimeout = -999999; + + # Do the wait. + oThread.start(); + msNow = base.timestampMilli(); + try: + rc = self.oTstDrv.oVBoxMgr.waitForEvents(cMsTimeout); + except: + reporter.errorXcpt(); + else: + msElapsed = base.timestampMilli() - msNow; + + # Check the return code and elapsed time. + if not isinstance(rc, int): + reporter.error('waitForEvents returns non-integer type after %u ms, expected 1' % (msElapsed,)); + elif rc != 1: + reporter.error('waitForEvents returned "%s" after %u ms, expected 1' % (rc, msElapsed)); + if msElapsed > 15000: + reporter.error('waitForEvents after %u ms, expected just above 2-3 seconds' % (msElapsed,)); + elif msElapsed < 100: + reporter.error('waitForEvents after %u ms, expected more than 100 ms.' % (msElapsed,)); + + oThread.join(); + oThread = None; + if reporter.testErrorCount() != 0: + break; + reporter.log('Iteration %u was successful...' % (i + 1,)); + return reporter.testDone()[1] == 0; + + +if __name__ == '__main__': + from tests.api.tdApi1 import tdApi1; + sys.exit(tdApi1([SubTstDrvPython1]).main(sys.argv)); + diff --git a/src/VBox/ValidationKit/tests/api/tdTreeDepth1.py b/src/VBox/ValidationKit/tests/api/tdTreeDepth1.py new file mode 100755 index 00000000..e69a7d17 --- /dev/null +++ b/src/VBox/ValidationKit/tests/api/tdTreeDepth1.py @@ -0,0 +1,249 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# $Id: tdTreeDepth1.py $ + +""" +VirtualBox Validation Kit - Medium and Snapshot Tree Depth Test #1 +""" + +__copyright__ = \ +""" +Copyright (C) 2010-2023 Oracle and/or its affiliates. + +This file is part of VirtualBox base platform packages, as +available from https://www.virtualbox.org. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation, in version 3 of the +License. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, see <https://www.gnu.org/licenses>. + +The contents of this file may alternatively be used under the terms +of the Common Development and Distribution License Version 1.0 +(CDDL), a copy of it is provided in the "COPYING.CDDL" file included +in the VirtualBox distribution, in which case the provisions of the +CDDL are applicable instead of those of the GPL. + +You may elect to license modified versions of this file under the +terms and conditions of either the GPL or the CDDL or both. + +SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +""" +__version__ = "$Revision: 156148 $" + + +# Standard Python imports. +import os +import sys +import random + +# Only the main script needs to modify the path. +try: __file__ +except: __file__ = sys.argv[0] +g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +sys.path.append(g_ksValidationKitDir) + +# Validation Kit imports. +from testdriver import base +from testdriver import reporter +from testdriver import vboxcon + + +class SubTstDrvTreeDepth1(base.SubTestDriverBase): + """ + Sub-test driver for Medium and Snapshot Tree Depth Test #1. + """ + + def __init__(self, oTstDrv): + base.SubTestDriverBase.__init__(self, oTstDrv, 'tree-depth', 'Media and Snapshot tree depths'); + + def testIt(self): + """ + Execute the sub-testcase. + """ + return self.testMediumTreeDepth() \ + and self.testSnapshotTreeDepth() + + # + # Test execution helpers. + # + + def testMediumTreeDepth(self): + """ + Test medium tree depth. + """ + reporter.testStart('mediumTreeDepth') + + try: + oVBox = self.oTstDrv.oVBoxMgr.getVirtualBox() + oVM = self.oTstDrv.createTestVM('test-medium', 1, None, 4) + assert oVM is not None + + # create chain with up to 64 disk images (medium tree depth limit) + fRc = True + oSession = self.oTstDrv.openSession(oVM) + cImages = random.randrange(1, 64); + reporter.log('Creating chain with %d disk images' % (cImages)) + for i in range(1, cImages + 1): + sHddPath = os.path.join(self.oTstDrv.sScratchPath, 'Test' + str(i) + '.vdi') + if i == 1: + oHd = oSession.createBaseHd(sHddPath, cb=1024*1024) + else: + oHd = oSession.createDiffHd(oHd, sHddPath) + if oHd is None: + fRc = False + break + + # modify the VM config, attach HDD + fRc = fRc and oSession.attachHd(sHddPath, sController='SATA Controller', fImmutable=False, fForceResource=False) + fRc = fRc and oSession.saveSettings() + fRc = oSession.close() and fRc + ## @todo r=klaus: count known hard disk images, should be cImages + + # unregister, making sure the images are closed + sSettingsFile = oVM.settingsFilePath + fDetachAll = random.choice([False, True]) + if fDetachAll: + reporter.log('unregistering VM, DetachAll style') + else: + reporter.log('unregistering VM, UnregisterOnly style') + self.oTstDrv.forgetTestMachine(oVM) + if fDetachAll: + aoHDs = oVM.unregister(vboxcon.CleanupMode_DetachAllReturnHardDisksOnly) + for oHD in aoHDs: + oHD.close() + aoHDs = None + else: + oVM.unregister(vboxcon.CleanupMode_UnregisterOnly) + oVM = None + + # If there is no base image (expected) then there are no leftover + # child images either. Can be changed later once the todos above + # and below are resolved. + cBaseImages = len(self.oTstDrv.oVBoxMgr.getArray(oVBox, 'hardDisks')) + reporter.log('API reports %i base images' % (cBaseImages)) + fRc = fRc and cBaseImages == 0 + if cBaseImages != 0: + reporter.error('Got %d initial base images, expected %d' % (cBaseImages, 0)); + + # re-register to test loading of settings + reporter.log('opening VM %s, testing config reading' % (sSettingsFile)) + if self.oTstDrv.fpApiVer >= 7.0: + # Needs a password parameter since 7.0. + oVM = oVBox.openMachine(sSettingsFile, "") + else: + oVM = oVBox.openMachine(sSettingsFile) + oVBox.registerMachine(oVM); + ## @todo r=klaus: count known hard disk images, should be cImages + + reporter.log('unregistering VM') + oVM.unregister(vboxcon.CleanupMode_UnregisterOnly) + oVM = None + + cBaseImages = len(self.oTstDrv.oVBoxMgr.getArray(oVBox, 'hardDisks')) + reporter.log('API reports %i base images' % (cBaseImages)) + fRc = fRc and cBaseImages == 0 + if cBaseImages != 0: + reporter.error('Got %d base images after unregistering, expected %d' % (cBaseImages, 0)); + + except: + reporter.errorXcpt() + + return reporter.testDone()[1] == 0 + + def testSnapshotTreeDepth(self): + """ + Test snapshot tree depth. + """ + reporter.testStart('snapshotTreeDepth') + + try: + oVBox = self.oTstDrv.oVBoxMgr.getVirtualBox() + oVM = self.oTstDrv.createTestVM('test-snap', 1, None, 4) + assert oVM is not None + + # modify the VM config, create and attach empty HDD + oSession = self.oTstDrv.openSession(oVM) + sHddPath = os.path.join(self.oTstDrv.sScratchPath, 'TestSnapEmpty.vdi') + fRc = True + fRc = fRc and oSession.createAndAttachHd(sHddPath, cb=1024*1024, sController='SATA Controller', fImmutable=False) + fRc = fRc and oSession.saveSettings() + + # take up to 200 snapshots (250 is the snapshot tree depth limit (settings.h:SETTINGS_SNAPSHOT_DEPTH_MAX)) + cSnapshots = random.randrange(1, 200); + reporter.log('Taking %d snapshots' % (cSnapshots)) + for i in range(1, cSnapshots + 1): + fRc = fRc and oSession.takeSnapshot('Snapshot ' + str(i)) + fRc = oSession.close() and fRc + oSession = None + reporter.log('API reports %i snapshots' % (oVM.snapshotCount)) + fRc = fRc and oVM.snapshotCount == cSnapshots + if oVM.snapshotCount != cSnapshots: + reporter.error('Got %d initial snapshots, expected %d' % (oVM.snapshotCount, cSnapshots)); + + # unregister, making sure the images are closed + sSettingsFile = oVM.settingsFilePath + fDetachAll = random.choice([False, True]) + if fDetachAll: + reporter.log('unregistering VM, DetachAll style') + else: + reporter.log('unregistering VM, UnregisterOnly style') + self.oTstDrv.forgetTestMachine(oVM) + if fDetachAll: + aoHDs = oVM.unregister(vboxcon.CleanupMode_DetachAllReturnHardDisksOnly) + for oHD in aoHDs: + oHD.close() + aoHDs = None + else: + oVM.unregister(vboxcon.CleanupMode_UnregisterOnly) + oVM = None + + # If there is no base image (expected) then there are no leftover + # child images either. Can be changed later once the todos above + # and below are resolved. + cBaseImages = len(self.oTstDrv.oVBoxMgr.getArray(oVBox, 'hardDisks')) + reporter.log('API reports %i base images' % (cBaseImages)) + fRc = fRc and cBaseImages == 0 + if cBaseImages != 0: + reporter.error('Got %d initial base images, expected %d' % (cBaseImages, 0)); + + # re-register to test loading of settings + reporter.log('opening VM %s, testing config reading' % (sSettingsFile)) + if self.oTstDrv.fpApiVer >= 7.0: + # Needs a password parameter since 7.0. + oVM = oVBox.openMachine(sSettingsFile, "") + else: + oVM = oVBox.openMachine(sSettingsFile) + oVBox.registerMachine(oVM); + reporter.log('API reports %i snapshots' % (oVM.snapshotCount)) + fRc = fRc and oVM.snapshotCount == cSnapshots + if oVM.snapshotCount != cSnapshots: + reporter.error('Got %d snapshots after re-registering, expected %d' % (oVM.snapshotCount, cSnapshots)); + + reporter.log('unregistering VM') + oVM.unregister(vboxcon.CleanupMode_UnregisterOnly) + oVM = None + + cBaseImages = len(self.oTstDrv.oVBoxMgr.getArray(oVBox, 'hardDisks')) + reporter.log('API reports %i base images' % (cBaseImages)) + fRc = fRc and cBaseImages == 0 + if cBaseImages != 0: + reporter.error('Got %d base images after unregistering, expected %d' % (cBaseImages, 0)); + except: + reporter.errorXcpt() + + return reporter.testDone()[1] == 0 + + +if __name__ == '__main__': + sys.path.append(os.path.dirname(os.path.abspath(__file__))) + from tdApi1 import tdApi1; # pylint: disable=relative-import + sys.exit(tdApi1([SubTstDrvTreeDepth1]).main(sys.argv)) |